﻿using System;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Windows.Forms;
using System.IO;
using System.Collections.Generic;
using System.Collections;
//using System.Net;
//using System.Net.Sockets;
using System.Threading;
using System.Runtime.Serialization.Formatters.Binary;
using System.Diagnostics;
using System.Collections.Specialized;
using System.Xml.Serialization;
using AbyssCombatLogger;
using SimpleListEditor;

namespace AbyssCombatLogger
{

    public partial class frmMeter : Form
    {
        private DateTime readTime = DateTime.MinValue;
            
        public const int IconHighLevelMob = 5;//should change to high level mob picture
        public const int IconCalculation = 4;
        public const int IconYourself = 3;
        public const int IconBoss = 2;
        public const int IconLowLevelMob = 1;
        public const int IconPlayer = 0;

        long startingBossHealth = 500000;
        long startingHighLevelMobHealth = 100000;
        long minTotalThreatTheshhold = 5000;
        long minThreatThresholdByPlayer = 5000;
        private float minThreatThresholdForSubTotal = 20000;

        float totalThreatOnTarget = 0f;
        float maxThreatOfPlayers = 0f;

        private string newLogContent = null;

        string playerName = "";

        string playerFile = "players.dat";
        string playersPlayedFile = "players-played.xml";
        
        /// <summary>
        /// private Server mServer = null;
        /// private Client mClient = null;
        /// </summary>

        private FileStream fStream0 = null;
        //TimeSpan dTime; //time between reads
        private SpellSearcher spellsSearcher = null;

        private delegate void ChangeTextDelegate(Control ctrl, string text);
        private delegate void ChangeSelectedObjectDelegate(Exception ex);
        private delegate void ChangeProgress(int progressAmount);



        Font subTotalFont = new Font(FontFamily.GenericSansSerif, 8, FontStyle.Bold);

        private NodeSorterByTag threatNodeSorter;
        private NodeSorterByTag damageAndHealingNodeSorter;

        private EventHandlers calc = null;

        TreeNode damageRoot = null;
        TreeNode healingRoot = null;

        bool cancelClicked = false;

        public bool CancelClicked
        {
            get { return cancelClicked; }
            set { cancelClicked = value; }
        }
        private string knownEnemiesFile = "known-enemies.xml";

        Dictionary<string, Player> playersList = new Dictionary<string,Player>();
        Dictionary<string, Enemy> enemiesList = new Dictionary<string,Enemy>();
        
        Point startMousePosFormSizer = Point.Empty;
        private long lastHealingByAllPlayers;
        private long lastDamageByAllPlayers; 
        
        Dictionary<EnemyPlayerPair, Rectangle> dBars = new Dictionary<EnemyPlayerPair, Rectangle>();

        string lastPlayerWithThreatOnTarget = null;
        long lastThreatAmount = 0;
        string maxedPlayer = null;//
        
        private const string subTotalKeyName = "TOTAL";
        private LogParser mParser = null;

        Point startMousePosition = Point.Empty;
        //string FocusTarget = null;
        string FocusAllString = "All Enemies";

        public string FocusTarget
        {
            get {
                if (cbHateFocusTarget.Text == FocusAllString || cbHateFocusTarget.Text == "")
                {
                    return null;
                }
                else return cbHateFocusTarget.Text;
            
            }
            set
            {
                if (value == null || value == "" || value == FocusAllString)
                    cbHateFocusTarget.Text = FocusAllString;
                cbHateFocusTarget.Text = value;

            }
        }


        private Brush YourBrushForTakingDamage = new HatchBrush(HatchStyle.Sphere, Color.Red);
        private int MAX_BarHeight = 20;

        Hashtable htOfClientRect = new Hashtable();

        Pen DamageBarPen = new Pen(Color.DarkBlue);
        Brush DamageFillBarBrush = Brushes.LightBlue;
        Pen HealingBarPen = new Pen(Color.DarkGreen);
        Brush HealingFillBarBrush = Brushes.GreenYellow;
        Pen ThreatBarPen = new Pen(Color.Red);
        Brush ThreatBarBrush = Brushes.DarkRed;

        Pen YourPenForBarBorder = new Pen(Color.YellowGreen);
        Brush YourBrushForBarFilling = Brushes.Yellow;

        Pen HatedTargetPenForBarBorder = new Pen(Color.RosyBrown);
        Brush HatedTargetBrushForBarFilling = Brushes.RosyBrown;
        //LinearGradientBrush(topPoint, bottomPoint, Color.White, Color.White)

        private System.Drawing.Brush fillBgBrush = Brushes.White;

        //controls
        protected SimpleListEditor.ListEditorControl<Enemy> EnemiesListEditorControl = null;
        protected SimpleListEditor.ListEditorControl<Player> PlayersListEditorControl = null;

        //end controls
        public class TagForMeterNodes
        {
            private string target;

            public string Target
            {
                get { return target; }
                set { target = value; }
            }
            private long amount;

            public long Amount
            {
                get { return amount; }
                set { amount = value; }
            }

            public TagForMeterNodes(string targetPlayerOrEnemyName, long amountThreatDamageOrHeal)
            {
                this.target = targetPlayerOrEnemyName;
                this.amount = amountThreatDamageOrHeal;
            }
        }
        /// <summary>
        ///  Constructor
        /// also doing first parse of log file
        /// </summary>
        public frmMeter()
        {
            InitializeComponent();

            openFileDialog1.CheckFileExists = true;
            openFileDialog1.CheckPathExists = true;
            openFileDialog1.Filter = "CombatLog*.txt|CombatLog*.txt";
            openFileDialog1.FilterIndex = 0;
            openFileDialog1.Title = "Open the A.O.C. Combat Log:";

            //tcAllPages.TabPages.Remove(tpServerClient); //do not show server client thing, its old and in the past
            
            this.oldCaptionFor_Player = tsmiAddTargetAsKnownPlayerAndRemoveThreatTable.Text;

            calc = new EventHandlers(ref pbParseStatus, ref tbMostRecentMessage,  ref playersList, ref enemiesList);//SHOULD ADD, ref enemiesList);
            ///////////////////////////////////
            EnemiesListEditorControl = new ListEditorControl<Enemy>(ref enemiesList);
            PlayersListEditorControl = new ListEditorControl<Player>(ref playersList);

            //this.scKnownPlayersAndEnemies.Panel1.Controls.Add(PlayersListEditorControl);
            //this.scKnownPlayersAndEnemies.Panel2.Controls.Add(EnemiesListEditorControl);
            this.groupBox_Enemies.Controls.Add(EnemiesListEditorControl);
            this.groupBox_Players.Controls.Add(PlayersListEditorControl);
            PlayersListEditorControl.Dock = DockStyle.Fill;
            EnemiesListEditorControl.Dock = DockStyle.Fill;
            
            //EnemiesListEditorControl.ItemTypeForCreating = typeof(Enemy);
            //PlayersListEditorControl.ItemTypeForCreating = typeof(Player);


            //this.scPVPTracker.Panel2.Controls.Add(this.RecentlyDetectedListEditorControl);

            //this.RecentlyDetectedListEditorControl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
            //           | System.Windows.Forms.AnchorStyles.Left)
            //           | System.Windows.Forms.AnchorStyles.Right)));


            //this.RecentlyDetectedListEditorControl.ButtonCaption = "List";
            //this.RecentlyDetectedListEditorControl.ListItems = ((System.Collections.Generic.Dictionary<string, object>)(resources.GetObject("RecentlyDetectedListEditorControl.ListItems")));
            //this.RecentlyDetectedListEditorControl.Location = new System.Drawing.Point(6, 33);
            //this.RecentlyDetectedListEditorControl.Name = "RecentlyDetectedListEditorControl";
            //this.RecentlyDetectedListEditorControl.Panel1Collapsed = false;
            //this.RecentlyDetectedListEditorControl.Panel2Collapsed = false;
            //this.RecentlyDetectedListEditorControl.Size = new System.Drawing.Size(160, 206);
            //this.RecentlyDetectedListEditorControl.TabIndex = 31;

            ///////////////////////////////////


            calc.Damage += new EventHandlers.HandleDamageDelegate(DoHandleDamageDone);

            //do EventCalculations init. first
            this.mParser = new LogParser(ref this.pbParseStatus, ref this.tbMostRecentMessage, ref this.calc, ref this.cancelClicked);

            threatNodeSorter = new NodeSorterByTag(); //for tvLogFileThreat sorter
            damageAndHealingNodeSorter = new NodeSorterByTag();
            tvLogFileThreat.TreeViewNodeSorter = threatNodeSorter;
            tvDamageAndHealing.TreeViewNodeSorter = damageAndHealingNodeSorter;

            //load some settings saved from last run, defaults were set in the settings.settings file for first time run
            this.Opacity = Properties.Settings.Default.Opacity;
            this.tbServerIP.Text = Properties.Settings.Default.ServerIPAddress;
            this.tscbPlayersPlayed.Text = Properties.Settings.Default.PlayerName;
            this.cbTracking.Checked = false;// Properties.Settings.Default.PVPTrackingEnabled;
            this.cboTrackMode.SelectedIndex = Properties.Settings.Default.PVPTrackingMode;

            Size fSize = Properties.Settings.Default.FormSize;
            if (fSize.Width != 0 && fSize.Height != 0)
                this.Size = fSize;
            this.TopMost = Properties.Settings.Default.WindowOnTop;
            tsmiWindowOnTop.Checked = this.TopMost;

            tsmiOpacity.Text = "Opacity: " + ((int)(this.Opacity * 100d)).ToString();
            Point desiredLocation = Properties.Settings.Default.MainWindowLocation;
            Point screenWorkTopLeft = Screen.PrimaryScreen.WorkingArea.Location;
            if (desiredLocation.X > screenWorkTopLeft.X+ Screen.PrimaryScreen.WorkingArea.Width
               || (desiredLocation.X + this.Width < 0 && desiredLocation.Y + this.Height< 0))
                desiredLocation = screenWorkTopLeft;
            this.Location = desiredLocation;
            Borderless(Properties.Settings.Default.FormBorderless);
            Color trnKeyColor = Color.White;

            tmrUpdateTreeViews.Interval = Properties.Settings.Default.TreeViewRefreshDelay;
            nudTreeViewRefreshInterval.Value = tmrUpdateTreeViews.Interval;

            tmrLogUpdater.Interval = Properties.Settings.Default.CombatReadDelay;
            nudParsePeriod.Value = Properties.Settings.Default.CombatReadDelay;

            tmrGraphUpdater.Interval = Properties.Settings.Default.GraphRefreshDelay;
            nudGraphUpdaterRefreshInterval.Value = Properties.Settings.Default.GraphRefreshDelay;

            if (Properties.Settings.Default.UseTransparencyKey)
                this.TransparencyKey = trnKeyColor;
            else
                this.TransparencyKey = Color.Empty;
            openFileDialog1.FileName = Properties.Settings.Default.LogFileName;

            damageRoot = tvDamageAndHealing.Nodes.Add("Damage Done", "Damage Done");
            healingRoot = tvDamageAndHealing.Nodes.Add("Healing Done", "Healing Done");

            this.Show();

            LoadPlayersPlayedFromFile();
            LoadKOSListFromFile();
            //LoadKnownPlayersFromFile();
            LoadKnownEnemiesFromFile();
            LoadPlayersFromFile();

            InfoLogIt("Loading spell database...");
            //load spell db
            spellsSearcher = new SpellSearcher();
            InfoLogIt("Finished loading " + spellsSearcher.SpellCount + " spells.");

            InfoLogIt("Please type /logcombat and choose '" + tsmiSetupLogWizard.Text + "' from the main menu to activate live parsing.");
            cbHateFocusTarget.SelectedIndex = 0; //all enemies
            //DoUseDefaultLogFile(true);

            //
            //CreateACLSscript();
        }

        private void LoadKnownEnemiesFromFile()
        {
            //throw new NotImplementedException();
            EnemiesListEditorControl.LoadFromBinaryFile(knownEnemiesFile, true);
        }

        private string CreateACLSscript()
        {
            //ScriptReporter reporter = GetNewScriptHandler();
            string aclScriptFile = GetGamePath() 
                + ScriptReporter.scriptRelativeToGamePath 
                + ScriptReporter.logOnAndOffScriptFileName;
            if (!File.Exists(aclScriptFile))
                ScriptReporter.CreateALogCombatScript(aclScriptFile);
            return aclScriptFile;
        }

        public string CurrentLogFile
        {
            get { return openFileDialog1.FileName; }
        }
        public string CurrentPath
        {
            get { return openFileDialog1.InitialDirectory; }
            set { openFileDialog1.InitialDirectory = value; }
        }
        public bool CurrentLogFileIsLatest(string path)
        {
            string currentLogFile = CurrentLogFile;
            string newestLogFileFound = FindNewestLog(path);
            return CurrentLogFile == newestLogFileFound; 
        }
        private bool FindLatestLogFile(bool allowEntireParse)
        {
            string path = FindAoCPath();
            if ( path == null)
            {
                InfoLogIt("Searched, but no AOC folder was found.\n" +
                    "Please click File and find your combat log.");
                openFileDialog1.InitialDirectory = "";
                //fswLogChanged.Path = "";

                ShowDialogToEnableLogging();
                cbLogging.Checked = false;
                return false;
            }
            else
            {
                //Directory Exists - try to find the combat log
                string newestFile = FindNewestLog(path);

                if (newestFile != null) //if found comat log files, pick one
                {
                    string dialogMsg =
                        "Found " + newestFile + "." +
                        " This looks like the latest log file due to its last modification date. Load it?\n";
                    DialogResult dr0 = System.Windows.Forms.MessageBox.Show(dialogMsg, "Would you like to use this file?", MessageBoxButtons.YesNo);
                       
                    //be sure to save this newest file before referencing the openFileDialog again.
                    openFileDialog1.FileName = newestFile;// theFullPathofFile;

                    if (dr0 == System.Windows.Forms.DialogResult.Yes)
                    {
                        bool success = OpenAndStartLogging(allowEntireParse, newestFile);
                        if (success)
                        {
                            //cbLogging.Checked = true;

                            InfoLogIt("Log file, " + newestFile + ", was found (by last date modified) and opened .\n", null, null);
                        }
                        else
                            cbLogging.Checked = false;
                        return true;
                    }
                    else
                    {
                        //user cannceled --do not open
                        InfoLogIt("No log file is currently opened. You will need to select a log file to enable log parsing.", null, "Warning: No log file is opened.");
                        cbLogging.Checked = false;
                        return false;
                    }
                    
                }
                else
                {
                    ShowDialogToEnableLogging();
                    cbLogging.Checked = false;
                    return false;
                }
            }
        }

        private string FindNewestLog(string path)
        {
            if (path == null || path=="") return null;

            //get all files in the found directory
            string[] allFiles = Directory.GetFiles(path);

            //make a list of all files starting with CombatLog, they are the files we are looking to parse
            const string filePrefix = "CombatLog";
            DateTime dtCreation = DateTime.MinValue;
            string newestFile = null;
            foreach (string f in allFiles)
            {
                string fileNoPath = GetFileNameFromFullPath(f);
                if (fileNoPath.StartsWith(filePrefix))
                {
                    DateTime dtFileCreation = File.GetCreationTime(f);
                    if (dtFileCreation > dtCreation)
                    {
                        dtCreation = dtFileCreation;
                        newestFile = f;
                    }
                }
            }
            return newestFile;
        }

        private static string GetFileNameFromFullPath(string f)
        {
            int lioBS = f.LastIndexOf("\\");
            string fileNoPath = f.Substring(lioBS + 1, f.Length - lioBS - 1);
            return fileNoPath;
        }
        private static string GetPathFromFullPath(string fullPath)
        {
            int lioBS = fullPath.LastIndexOf("\\");
            string fileNoPath = fullPath.Substring(0, lioBS + 1);
            return fileNoPath;
        }
        private string FindAoCPath()
        {
            InfoLogIt("Finding Latest Log File...");

            //combine combinations of paths and drives too look for possible AOC locatoins
            string[] drives = Environment.GetLogicalDrives();

            const string AocDir = "\\Funcom\\Age of Conan";

            List<string> paths = new List<string>();

            string currentlySelectedPath = null;
            if (openFileDialog1.FileName != null && openFileDialog1.FileName != "")
            {
                int lioBS = openFileDialog1.FileName.LastIndexOf("\\");
                if (lioBS > 0)
                    currentlySelectedPath = openFileDialog1.FileName.Substring(0, lioBS);
            }
            if (currentlySelectedPath != null && currentlySelectedPath != "")
                paths.Add(currentlySelectedPath);

            //add these paths last so that the last path used is searched first
            paths.Add(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) + AocDir);
            paths.Add(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86) + AocDir);


            int possiblePathsCount = paths.Count();
            InfoLogIt("Scanning a total of " + possiblePathsCount + " possible known Age of Conan directories to find the combat log.\n");

            //scan for available aoc directories
            bool dirExists = false;
            string thePath = null;
            foreach (string path in paths)
            {
                dirExists = Directory.Exists(path);
                if (dirExists)
                {
                    //openFileDialog1.InitialDirectory = path;
                    //fswLogChanged.Path = path;
                    thePath = path;
                    break;//found it
                }
            }
            return thePath;
        }

        private void ShowDialogToEnableLogging()
        {

            //also it would be good to enable watching 
            fswLogChanged.EnableRaisingEvents = true;

            string step1 = " Please do the following to find your log file:" +
            " but the only way you can tell is to type turn off and back on combat logging." +
            " FJust type \n           \"/logcombat off\"<Enter> and " + "\"/logcombat on\"<Enter> \n" +
            " Where <Enter> means press the enter or \"return\" key.\n" +
            " The Age of Conan combat log file will be shown (as a system message, I think, if you subscribe to system messages) in one of your tab windows.\n";

            System.Windows.Forms.MessageBox.Show(step1, "Please enable logging in Age of Conan", MessageBoxButtons.OK);
            
        }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="allowEntireParse"></param>
    /// <param name="logFileName"></param>
    /// <returns>false if unsuccessful, true otherwise</returns>
        private bool OpenLogAndStuff(bool allowEntireParse, string logFileName)
        {
            //if one is not found a default will be made!
            if (logFileName == "" || logFileName == null)
            {
                //throw new Exception("No Log file has been configured yet.");
                return false;
            }
            else
            {
                //combat log is saved in our properites...
                int lioDirSep = logFileName.LastIndexOf("\\");
                if (lioDirSep != -1) //if \ exists in the string
                {
                    openFileDialog1.FileName = logFileName;
                    string filePath = logFileName.Substring(0, lioDirSep);
                    fswLogChanged.Path = filePath;//file path without file name
                }
                else
                {
                    try
                        { openFileDialog1.FileName = logFileName; }
                    catch (Exception fIt){
                        Debug.WriteLine(fIt.ToString());
                    }
                }
                //filter should be focused on the the combat log itself. Its name is sufficient since the path is specified elsewhere.
                fswLogChanged.Filter = "CombatLog*.txt|CombatLog*.txt"; //logFileName.Substring(lioDirSep + 1, logFileName.Length - lioDirSep - 1);
                //ps this doesnt seem to work enough times, so i still do the parse on a timer. But it fires occassionally so I leave it in there.
                    

                if (allowEntireParse)
                {
                    //DialogResult dr = System.Windows.Forms.MessageBox.Show("Do you want to parse the entire file now?", "You have loaded the log file. Did you want reparse the log file?", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                    //&& dr== System.Windows.Forms.DialogResult.Yes
                        
                    this.mParser.charCount = 0;
                        
                    newLogContent = GetNewLogContent(openFileDialog1.FileName, true);

                    //BEGIN: PROGRESS BAR
                    //Setup a nice looking load screen with a progress bar so user sees progress.
                    ParseProgessBarOn(true);

                    string oldText = this.Text;
                    this.Text = "ACL is loading and parsing the combat log...";
                    bool canceled = ParseAndSaveNewData(newLogContent);
                    this.Text = oldText;

                    ParseProgessBarOn(false);
                    //END: PROGRESS BAR CODE

                    ////newLogContent = GetNewLogContent(openFileDialog1.FileName);
                    //if (newLogContent != null && newLogContent.Trim().Length != 0)
                    //{
                    //    ParseAndSaveNewData(newLogContent);
                    //    CreateTreeViews(true);
                    //}
                    //charCount = newLogContent.Length;
                    //CreateTree();
                    if (!canceled)
                    {
                        InfoLogIt("Combat log successfully parsed the file contents!\n");
                        return true;
                    }
                    else
                        return false;
                }
                else
                {
                    //need to set data to null otherwise it will be parsed no matter what when the timer comes up.
                    newLogContent = null;
                    SetLogCursorToEOF(logFileName);
                    return true;
                }

            }
            //}
            //catch(Exception ex1)
            //{
            //    const string game = "Age of Conan";
            //    InfoLogIt("Please locate the log file used by " + game + " by clicking 'Open File' and navigating to your AOC folder. This file is has a .txt extension. If the file does not exist chances are you need to execute the '/logcombat on' command in the " + game + " console window.", ex1, "Please open a combat log file.");
            //    return false;
            //}
        }

        private void DoneLoading()
        {
            InfoLogIt("Done loading log file.", null, null);
            SimpleMessage("Done loading!");
        }

        private long FindDamageByTarget(List<ParsedLogEvent> lastDamageTargetPairs, string key)
        {
            for (int idx = 0; idx < lastDamageTargetPairs.Count; idx++)
            {
                if (lastDamageTargetPairs[idx].Target == key)
                    return lastDamageTargetPairs[idx].AmountInt;
            }
            return -1;
        }
        /// <summary>
        /// opEN A FILE AND SET THE GAME PATH
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tsmiOpenFile_Click(object sender, EventArgs e)
        {
            cbSimulationEnabled.Checked = false;
            cbSimulationEnabled.Enabled = false;
            OpenUserSpecifiedFile();
        }

        private void OpenUserSpecifiedFile()
        {

            DialogResult dr0 = openFileDialog1.ShowDialog();

            if (dr0 == DialogResult.OK)
            {
                OpenSpecifiedFile(true);

            }
        }

        private void OpenSpecifiedFile(bool entireParse)
        {
            bool oldTrackingSetting = cbTracking.Checked;
            EnableTracking(false);
            //htDamageDealers.Clear(); //dont clear server saved stats,local only

            InfoLogIt("Damage, healing, and threat were cleared.");
            damageRoot.Nodes.Clear(); //clear tree view nodes
            healingRoot.Nodes.Clear();
            tvLogFileThreat.Nodes.Clear();
            calc.htGroupThreatFromLog.Clear();
            calc.htTotalDamagesByPlayer.Clear();
            calc.htTotalHealsByPlayer.Clear();

            tmrUpdateTreeViews.Enabled = cbSimulationEnabled.Checked;
            tmrLogUpdater.Enabled = false;
            tmrGraphUpdater.Enabled = cbSimulationEnabled.Checked;
            cbLogging.Enabled = !cbSimulationEnabled.Checked;
            //tcAllPages.SelectedTab = tpInfo;//show this tab because it has the progress bar on it
            //firstStamp = true;

            //save open file setting
            Properties.Settings.Default.LogFileName = openFileDialog1.FileName;
            Properties.Settings.Default.Save();

            if (OpenAndStartLogging(entireParse, openFileDialog1.FileName))
            {
                //tmrLogUpdater.Enabled = true;
                //tmrUpdateTreeViews.Enabled = true;
                //cbLogging.Checked = false;
                InfoLogIt("Log parsing completed successfully.", null, "Log parsing complete.");
                InfoLogIt("Start script created as " + CreateACLSscript());
                EnableTracking(oldTrackingSetting);

                //TurnTimersEnabled(true);
                tmrGraphUpdater.Enabled = true;
            }
            else
            {
                //F
                //cbLogging.Checked = false;
            }                //save open file setting

            UpdateNecessaryPages(true);

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="entireFileParse"></param>
        /// <param name="logFileName"></param>
        /// <returns>true if successful false if not or canceled</returns>

        bool opened = false;
        private bool OpenAndStartLogging(bool entireFileParse, string logFileName)
        {
            //if (!opened)
            //{
                opened = OpenLogAndStuff(entireFileParse, logFileName);

                if (opened)
                {
                    TurnTimersEnabled(false);
                    playerName = tscbPlayersPlayed.Text;

                    //
                    ParseProgessBarOn(true);

                    newLogContent = GetNewLogContent(logFileName, entireFileParse);
                    bool canceled = false;
                    if (newLogContent != null && newLogContent.Length != 0)
                    {
                        canceled = ParseAndSaveNewData(newLogContent);
                    }

                    DoneLoading(); //post message                
                    ParseProgessBarOn(false);

                    string path = GetPathFromFullPath(logFileName);
                    if (CurrentLogFileIsLatest(path))
                    {
                        TurnTimersEnabled(true);
                        cbLogging.Checked = true;
                    }
                    else
                        cbLogging.Checked = false;

                    return !canceled;
                }
                else
                    return false;
            //}
            //return false;
        }
        
        private void ParseProgessBarOn(bool goOn)
        {
            //toolStripMain.Enabled = !goOn;
            //tcAllPages.Enabled = !goOn;
            btnCancel.Visible = goOn;
        }
        private void SetLogCursorToEOF(string logFileName)
        {
            try
            {
                ///http://bytes.com/topic/c-sharp/answers/570158-need-shared-access-file-c-c
                ///No, you don't. You are trying to open the file in shared read mode (FileShare.Read), which
                ///isn't possible as the file is already open in shared read/write mode, so, you need to share
                ///the file for read/write too -- Willy.
                fStream0 = File.Open(logFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                this.mParser.fileLength = fStream0.Length;
                fStream0.Close();
                this.mParser.charCount = this.mParser.fileLength;
            }
            catch (Exception exGnrl)
            {
                InfoLogIt("An exception occured and the program cannot open the file. : \t", exGnrl, "Cannot open log file.");
               // System.Windows.Forms.MessageBox.Show("");
            }

        }
        //try to get it so cursor works
        private string GetNewLogContent(string logFileName, bool LoadFromStartOfFile)
        {
            //string lastNewContent = newLogContent;

            ///http://bytes.com/topic/c-sharp/answers/570158-need-shared-access-file-c-c
            ///No, you don't. You are trying to open the file in shared read mode (FileShare.Read), which
            ///isn't possible as the file is already open in shared read/write mode, so, you need to share
            ///the file for read/write too -- Willy.

            try
            {
                if (fStream0 != null)
                    fStream0.Close();

                fStream0 = File.Open(logFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                long lastFileLength = this.mParser.fileLength;
                    

                //gives me pain...ahhh create
                int blockByteCount;
                if (LoadFromStartOfFile)
                {
                    lastFileLength = 0;
                    blockByteCount = (int)(fStream0.Length);
                }
                else
                    blockByteCount = (int)(fStream0.Length - lastFileLength);
                //block size now calculated, now if necessary seek and retreive (read) the new information from the log file.

                if (blockByteCount > 0)
                {
                    fStream0.Seek(lastFileLength, SeekOrigin.Begin);
                    byte[] newContents = new byte[blockByteCount];
                    fStream0.Read(newContents, 0, blockByteCount);
                    
                    DateTime lastReadTime = readTime;
                    readTime = DateTime.Now;
                    newLogContent = ASCIIEncoding.ASCII.GetString(newContents);
                }
                else if (blockByteCount < 0)
                {
                    Debug.Assert(false);
                }
                else
                {
                    newLogContent = null;
                }

                this.mParser.fileLength = fStream0.Length;
                //changed this: do not close yet we are gonna attempt to see if the code updates
                fStream0.Close();
                //
                //tbData.Text = "Log file: " + logFileName + " was opened, read, and closed successfully.\n";

                return newLogContent;

            }
            catch (IOException ioEx)
            {
                //System.Windows.Forms.MessageBox.Show("An I/O exception occured and the program cannot open the file. Check the file exists and try again.:" + ioEx.ToString());
                //TURN TIMER OFF IF CANT OPEN FILE
                //this.DoUpdates = false;
                //tsbPauseRecord.Text = "Run now! (stopped/error)";
                return null;
            }
            catch (Exception exGnrl)
            {
                //System.Windows.Forms.MessageBox.Show("An exception occured and the program cannot open the file. : \n\n" + exGnrl.ToString());
                // tsbPauseRecord.Text = "Run now! (stopped/error)";
                return null;
            }
        } //end of method!

        private void CloseEverything()
        {
            ////if (mClient != null)
            ////    mClient.CloseEverything();
            ////if (mServer != null)
            ////    mServer.CloseEverything();

            if (fStream0 != null)
            {
                this.mParser.fileLength = 0;
                fStream0.Close();
            }
        }

        bool bFormClosingAlreadySaved = false;
        private void frmMeter_FormClosing(object sender, FormClosingEventArgs e)
        {
            Exiting = true;

            if (bFormClosingAlreadySaved)
                return;
            //COMMENTED NETWORK CODE:CloseEverything();
            TurnTimersEnabled(false);


            //save settings
            Properties.Settings.Default.LogFileName = openFileDialog1.FileName;
            Properties.Settings.Default.PlayerName = tscbPlayersPlayed.Text;
            Properties.Settings.Default.Opacity = this.Opacity;
            Properties.Settings.Default.MainWindowLocation = this.Location;
            Properties.Settings.Default.ServerIPAddress = this.tbServerIP.Text;
            Properties.Settings.Default.FormSize = (Size)(this.Size);
            Properties.Settings.Default.FormBorderless = tsmiBorderless.Checked;
            Properties.Settings.Default.UseTransparencyKey = tsmiUseTransparentKey.Checked;
            Properties.Settings.Default.WindowOnTop = this.TopMost;
            Properties.Settings.Default.PVPTrackingEnabled = this.cbTracking.Checked;
            Properties.Settings.Default.PVPTrackingMode = this.cboTrackMode.SelectedIndex;

            Properties.Settings.Default.TreeViewRefreshDelay = tmrUpdateTreeViews.Interval;
            Properties.Settings.Default.CombatReadDelay = tmrLogUpdater.Interval;
            Properties.Settings.Default.GraphRefreshDelay = tmrGraphUpdater.Interval;

            Properties.Settings.Default.Save();

            SaveKnownEnemiesToFile();
            SavePlayersPlayedToFile();
            SavePlayersToFile();
            SaveKOSListToFile();

            mParser.CancelParse = true;
            mParser = null;
            bFormClosingAlreadySaved = true;
            //this.Close();
            if (System.Windows.Forms.Application.MessageLoop)
            {
                // Use this since we are a WinForms app
                System.Windows.Forms.Application.Exit();
            }
            else
            {
                // Use this since we are a console app
                System.Environment.Exit(1);
            }

            //Application.Exit();
            //Thread.CurrentThread.Abort();
        }

        private void SaveKOSListToFile()
        {
            KOSPlayerInfo[] allPlayers = new KOSPlayerInfo[kosList.Count];
            int idx = 0;
            foreach (string key in kosList.Keys)
                allPlayers[idx++] = kosList[key];

            SaveToBinaryFile(allPlayers, KOSListFile);
        }

        /*COMMENTED NETWORK CODE:
        private void CloseEverything()
        {

            if (mServer != null)
                mServer.CloseEverything();
            if (mClient !=null)
                mClient.CloseEverything();
        }
        */

        private void TurnTimersEnabled(bool enabled)
        {
            cbLogging.Checked = enabled;
            //Perhaps we should loop through form all timers in form control and enable that way so we dont always have to add to this list.
            this.tmrLogUpdater.Enabled = enabled;
            //this.tmrServerQueueHandler.Enabled = enabled;
            //this.tmrUpateNetworkStatus.Enabled = enabled;
            this.tmrUpdateTreeViews.Enabled = enabled;
            this.tmrGraphUpdater.Enabled = enabled;
        }
        
        /// <summary>
        ///  The newLine argument to parse can be multi-line by seperating them with delimeters
        /// </summary>
        /// <param name="newLine"></param>
        /// <returns> true if successful</returns>
        private bool ParseAndSaveNewData(string newLine)
        {
            return ((LogParser)mParser).ParseAndSaveNewData(newLine);
        }

        private List<string> GetStringsFromSingleAOCLine(string newLine)
        {
            //Custom line finder / stripper / string list maker
            int iEnd = 0;
            int iStart = -1;
            bool bEnd = false;
            List<string> lstLines = new List<string>();//newLine.Split(new char[] { '\r' }, ).ToList<string>(); //split into lines delimited by new line character
            
            
            do
            {
                iStart = iEnd + 1;

                //first newline defines end of line and possible start of new line
                //therefore index will give where to strip the characters and also to 
                int ioDelimChars = newLine.IndexOf("\r\n");

                //if (iEnd == -1 || iStart > newLine.Length)
                if (ioDelimChars == -1 || iEnd == -1 || iStart > newLine.Length)
                {
                    bEnd = true;
                    iEnd = -1;// newLine.Length; //becuase not needed
                } else
                {
                    //
                    if (ioDelimChars != -1)
                    {
                        iEnd = ioDelimChars;//two chars for \r\n if you want it add 2
                        if (iEnd > iStart || iStart > iEnd)
                        {
                            //do nothing for now!
                            //Debug.WriteLine(
                            //@"Information may need to be added because it was removed for the purpose of 
                            //removing an error in .net if not accounted for.
                            //For refernce: an aimbiguous operation has occured:");
                            //if (iEnd > iStart)
                            //   Debug.WriteLine("(iEnd > iStart)");
                            // if (iStart > iEnd)
                                Debug.WriteLine("(iStart > iEnd)");
                        }
                        else
                        {
                            lstLines.Add(newLine.Substring(iStart, iEnd - iStart));
                        }
                    }
                    else
                    {
                        iEnd = newLine.Length;
                        lstLines.Add(newLine.Substring(iStart, iEnd));
                    }
                }

            } while (!bEnd);

            return null;
        }

        void mServer_ServerHandleClientCommError(Exception e)
        {
            InfoLogIt(e.ToString());
        }

        /*COMMENTED NETWORK CODE:
        private void cbServerRunning_CheckedChanged(object sender, EventArgs e)
        {
            if (cbServerRunning.Checked)
            {
                if (mServer == null)
                {
                    IPAddress serverExternalIP = IpFinder.GetExternalIp();
                    txtWhatIsMyIP.Text = serverExternalIP.ToString();

                    int port = (int)(nudServerStartPort.Value);
                    mServer = new Server(port, ref this.htDamageDealers);
                    
                    //mServer.ClientSentToServer += new Server.NetworkMessage(mServer_ClientSentToServer);
                    mServer.ClientLoggedOn += new Server.ClientNameMessage(mServer_ClientLoggedOn);
                    mServer.ClientLoggedOff += new Server.ClientNameMessage(mServer_ClientLoggedOff);
                    mServer.ServerHandleClientCommError += new Server.ServerError(mServer_ServerHandleClientCommError);

                    try
                    {
                        mServer.Start();
                        tmrServerQueueHandler.Enabled = true;
                        tmrTargetDamageFromLog.Enabled = true;
                        nudServerStartPort.Enabled = false;
                        InfoLogIt("Server successfully created.\n", null, "Server started.");
                    }
                    catch (Exception ex1)
                    {
                        InfoLogIt("Cannot start the new server. Check the port and try again.\n", ex1, "Cannot start the server");
                        cbServerRunning.Checked = false;
                    }
                }

            }
            else
            {
                tmrServerQueueHandler.Enabled = false;
                tmrTargetDamageFromLog.Enabled = true;
                nudServerStartPort.Enabled = true;
            }
        }
        
        bool mServer_ClientLoggedOff(string playerName)
        {
            if (playerName != null && playerName != "")
            {
                if (htClientPlayers.ContainsKey(playerName))
                {
                    htClientPlayers.Remove(playerName);
                    InfoLogIt(playerName + " has logged out.\n");
                    Speaker.Speak(playerName + " left.");
                }
                return true;
            }
            else
            {
                Debug.WriteLine("Player logged in with an empty name" + playerName + " which could cause problems.");
                return false;
            }
        }

        bool mServer_ClientLoggedOn(string playerName)
        {
            //no longer need a hash table and can convert this to a list since we have the player names with each transfer to server, and it works better than the client id which was just a hash code
            if (playerName != null && playerName != "")
            {
                if (!htClientPlayers.ContainsKey(playerName))
                    htClientPlayers.Add(playerName, playerName); //client player saved!
                InfoLogIt(playerName + " has logged on.\n");
                Speaker.Speak(playerName + " entered.");
                return true;
            }
            else
            {
                Debug.WriteLine("Player logged in as " + playerName + " which could cause problems.");
                return false;
            }
        }
        */

        private void tvThreat_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button==System.Windows.Forms.MouseButtons.Right)
                cmsThreatMeterCommands.Show(tvLogFileThreat, e.Location);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns>true if not canceled, false if not logging or content was null</returns>
        private bool DoHandleLogChange()
        {
            //if (IsLogging)
            {
                newLogContent = GetNewLogContent(openFileDialog1.FileName, false);
                if (newLogContent != null && newLogContent.Length != 0)
                {
                    lastDataRecieved = DateTime.Now;
                    return ParseAndSaveNewData(newLogContent);
                }
             
            }
            return false;
        }

        float timeBeforeDisabling = 120000; ///in milliseconds.

        public float TimeBeforeDisabling_InMs
        {
            get { return timeBeforeDisabling; }
            set { timeBeforeDisabling = value; }
        }

        int timeOutCounter = 0;

        private void tmrLogUpdater_Tick(object sender, EventArgs e)
        {
            //bool btrash = CombatParsingActivity;
            //if (CombatParsingActive )
           // {
                int timeOutPeriod = (int)(timeBeforeDisabling / tmrLogUpdater.Interval + .5);
                
                bool bWasData = DoHandleLogChange();

                //increment the number of times there was not data.
                bool notData = !bWasData;
                if (notData) timeOutCounter++;
                else timeOutCounter = 0;


            //    //{
            //    //less stringent conditions to shut down the logger
            //    float numCyclesPerReset = timeBeforeDisabling / tmrLogUpdater.Interval;
            //    if (SafeAutoDisableLogging && timeOutCounter > numCyclesPerReset 
            //        && CurrentLogFileHasNotChanged && cbLogging.Enabled == true)
            //    {
            //        //ClearRaidThreat();
            //        //btnClearAllThreatNow.Visible = true;
            //        //Thread.CurrentThread.Join(wait_time_before_button_dissapears);
            //        //btnClearAllThreatNow.Visible = false;
            //        //SimpleMessage("Clearing threat is reccomended.");
                    
            //        //More important than clearing threat is disable the polling when not needed.
            //        SimpleMessage("Combat logging was automatically disabled. Disabling this option is not reccomended.");
            //        TurnTimersEnabled(false);
                    
            //        //disable logging
            //    }
            //}

            //    if (CombatTimeout_Ticks_Max AutoClearAggroWhenCombatTimesOut)
            //    {

            //        ClearRaidThreat();
            //    }
            //    //auto clear aggro tables as out of combat
            //}

    }

        private void ParseOpacityInput()
        {
            string opacityContents = tsmiOpacity.Text;
            opacityContents = opacityContents.Replace("%", "");
            opacityContents = opacityContents.Replace("Opacity:", "").Trim();

            //hehehe, here we g000!
            double dValue = double.Parse(opacityContents) / 100d;
            try
            {
                if (dValue < .15d)
                {
                    this.Opacity = .15d;
                    tsmiOpacity.Text = "Opacity: " + (int)(this.Opacity * 100) + "%";
                }
                else
                {
                    this.Opacity = dValue;
                    tsmiOpacity.Text = "Opacity: " + (int)(dValue*100) + "%";
                }
            }
            catch (Exception userError)
            {
                //this.Opacity = 1d;
                //tsmiOpacity.Text= "100%";
                tsmiOpacity.Select();
                //InfoLogIt(userError.Message);
            }
        }

        //Reference:
        //Title: "Handling the dreaded cross-thread Exception"
        //Link: http://www.dreamincode.net/forums/blog/143/entry-2337-handling-the-dreaded-cross-thread-exception/
        //last accessed: Oct 14, 2010

        private void ChangeSelectedObject(Exception lastException)
        {
            if (pgLastException.InvokeRequired)
            {
                pgLastException.Invoke(new ChangeSelectedObjectDelegate(ChangeSelectedObject), new object[] { pgLastException, lastException });
            }
            else
                pgLastException.SelectedObject = lastException;

            //pgLastException.Update();
        }

        public static void ChangeText(Control ctrl, string str)
        {
            if (!ctrl.IsDisposed)
            {
                if (ctrl.InvokeRequired)
                {
                    ctrl.Invoke(new ChangeTextDelegate(ChangeText), new object[] { ctrl, str });
                }
                else
                {
                    ctrl.Text = str;
                    ctrl.Update();
                }
            }

        } //end of reference (about 10 lines)

        public void InfoLogIt(string p)
        {
            InfoLogIt(p, null, null);
        }
        Exception lastException = null;

        System.Speech.Synthesis.SpeechSynthesizer infologSpeaker = new System.Speech.Synthesis.SpeechSynthesizer();
        /// <summary>
        /// 
        /// </summary>
        /// <param name="p">Message to add to log and display in recent tb</param>
        /// <param name="anyExceptions">Exceptions that occured that may be viewed by the program user.</param>
        /// <param name="speakMessageIfAny">A message to speak using text-to-speech.</param>
        public void InfoLogIt(string p, Exception anyExceptions, string speakMessageIfAny)
        {
            if (p == null) p = "";
            
            TimeZone tzlocal = System.TimeZone.CurrentTimeZone;
            DateTime dt = tzlocal.ToLocalTime(DateTime.UtcNow);

            string exMessage = anyExceptions == null ? "" : anyExceptions.Message;
            if (this.IsDisposed == false)
            {
                if (exMessage != "")
                    p = p + "\r\n"+ exMessage + "\r\n";
                else
                    p = p + "\r\n";
                
                string newText = tbData.Text + "   [" + dt.ToLongTimeString() + "] " + p;
                ChangeText(tbData, newText);

                if (anyExceptions != null)
                {
                    lastException = anyExceptions;
                    ChangeSelectedObject(lastException);
                }

                if (speakMessageIfAny != null && speakMessageIfAny != "")
                {
                    infologSpeaker.SpeakAsync(speakMessageIfAny);
                }
                tbData.SelectionStart = tbData.Text.Length;
                tbData.ScrollToCaret();
            }
        }
        private void SimpleMessage(string simpleRecentMessage)
        {
            ChangeText(tbMostRecentMessage, simpleRecentMessage); //for now this message will not be logged it is a friendly message.
        }

        private void tsmiOpacity_DoubleClick(object sender, EventArgs e)
        {
            ParseOpacityInput();
        }

        private void tsmiOpacity_Leave(object sender, EventArgs e)
        {
            ParseOpacityInput();
        }

        private void tsmiOpacity_Enter(object sender, EventArgs e)
        {
            ParseOpacityInput();
        }

        private void tsmiOpacity_Validated(object sender, EventArgs e)
        {
            ParseOpacityInput();
        }

        private void tpRaid_Click(object sender, EventArgs e)
        {

        }

        private void tsmiUseDefaultLog(object sender, EventArgs e)
        {            //htDamageDealers.Clear(); //dont clear server saved stats,local only
            DoUseDefaultLogFile(false);
        }

        private void DoUseDefaultLogFile(bool allowEntireParse)
        {

            if (FindLatestLogFile(allowEntireParse))
            {
                OpenSpecifiedFile(false);
                //tmrLogUpdater.Enabled = true;
            }
            //{
            //    damageRoot.Nodes.Clear(); //clear tree view nodes
            //    healingRoot.Nodes.Clear();

            //    tvLogFileThreat.Nodes.Clear();
            //    calc.htGroupThreatFromLog.Clear();

            //    tmrUpdateTreeViews.Enabled = false;
            //    tmrLogUpdater.Enabled = false;

            //    bool open = OpenAndStartLogging(true, openFileDialog1.FileName);

            //    if (open)
            //    {
            //        ParseProgessBarOn(true);
            //        newLogContent = GetNewLogContent(openFileDialog1.FileName, allowEntireParse);
            //        bool canceled = false;
            //        if (newLogContent != null && newLogContent.Length != 0)
            //        {
            //            canceled = ParseAndSaveNewData(newLogContent);
            //        }

            //        DoneLoading(); //post message                
            //        ParseProgessBarOn(false);

            //        cbEncounterDetected.Checked = true;
            //        tmrLogUpdater.Enabled = true;
            //        tmrUpdateTreeViews.Enabled = true;
            //        Properties.Settings.Default.LogFileName = openFileDialog1.FileName;
            //        Properties.Settings.Default.Save();
            //        //EnableTracking(false);
            //        UpdateNecessaryPages(true, true);
            //    }
            //}
            //else
            //{
            //    Debug.Assert(true);
            //}
        }

        private void tsmiOpacity_Click(object sender, EventArgs e)
        {
            //tsmiOpacity.Text = this.Opacity * 100 + "%";
            //ParseOpacityInput();
        }

        private void tsmiOpacity_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                ParseOpacityInput();
            }
        }

        /*COMMENTED NETWORK CODE:
        private void tmrUpateNetworkStatus_Tick(object sender, EventArgs e)
        {
            TreeNode tn = tvNetworkStatus.Nodes["nClientsRoot"];
            tn.Nodes.Clear();
            foreach (string player in htClientPlayers.Keys)
            {
                TreeNode clientNode = new TreeNode(player);
                tn.Nodes.Add(clientNode);
            }
        }
         */

        private void UpdateTargetsTreeView()
        {
            if (this.IsDisposed)
                return;

            Dictionary<string, List<ParsedLogEvent>> targetting = this.dDamageEventsOnTargets;

            List<string> targetsMatch= new List<string>() ;
            foreach (TreeNode t in tvTargets.Nodes)
            {
                targetsMatch.Add(t.Text);
            }
            
            foreach (string key in targetting.Keys)
            {
                TreeNode tnTarget = null;
                List<ParsedLogEvent> targetsList= targetting[key];

                if (targetsList.Count > 0)
                {
                    if (tvTargets.Nodes.ContainsKey(key))
                    {
                        tnTarget = tvTargets.Nodes[key];
                        tnTarget.Text = key;
                        tnTarget.Nodes.Clear();
                    }
                    else
                    {
                        tnTarget = tvTargets.Nodes.Add(key, key);
                    }

                    foreach (ParsedLogEvent e in targetsList)
                    {
                        tnTarget.Nodes.Add(e.Target, e.Target);
                    }

                    targetsMatch.Remove(key);
                }
            }


            foreach (string tarToRemove in targetsMatch)
            {
                tvTargets.Nodes.RemoveByKey(tarToRemove);
            }

            tvTargets.ExpandAll();
            tvTargets.Update();
        }

        long totalDamageByAllPlayers = 0;
        long totalHealingByAllPlayers = 0;
        TreeNode healsTotalNode = null;
        TreeNode damagesTotalNode = null;
        private void UpdateDamageAndHealingTreeView(ref Hashtable htTotalDamagesByPlayer, ref Hashtable htTotalHealsByPlayer)
        {
            //Debug.Assert(tvDamageAndHealing.Nodes.Count != 0);
            totalDamageByAllPlayers = 0;
            totalHealingByAllPlayers = 0;
            //data

            //bool removeEmpty = false;
            ////calculate total damage, and remove any bogus data on site... in future make sure GI doesnt happen in GIGO
            foreach (string player in htTotalDamagesByPlayer.Keys)
            {
            //    if (player == null || player == "" && !player.Contains("'"))
            //        removeEmpty = true;
            //    else
            //        if (!FindInKnownEnemiesList(player)) //dont add say a bosses damage to the total or it will fuck up the group totals.
                        totalDamageByAllPlayers += (long)(htTotalDamagesByPlayer[player]);
            }
            //if (removeEmpty)
            //    htTotalDamagesByPlayer.Remove(""); //shouldnt be there

            //removeEmpty = false; //reset flag
            ////calculate total heals and remove bogus data
            foreach (string player in htTotalHealsByPlayer.Keys)
            {
            //    if (player == null || player == "")
            //        removeEmpty = true;
            //    else
            //        if (!FindInKnownEnemiesList(player))
                        totalHealingByAllPlayers += Convert.ToInt64 (htTotalHealsByPlayer[player]);
            }
            //if (removeEmpty)
            //    htTotalHealsByPlayer.Remove(""); //shouldnt be there
            //removeEmpty = false;


            //FIRST DO DAMAGE (HEALS TO FOLLOW)
            //now show the total damage by each player

            if (pbParseStatus.Visible)
            {
                //SimpleMessage("Populating tree view with " + htTotalDamagesByPlayer.Count + " damages.");

                pbParseStatus.Maximum = htTotalDamagesByPlayer.Count;
                pbParseStatus.Value = 0;
            }
            foreach (string player in htTotalDamagesByPlayer.Keys)
            {
                if (pbParseStatus.Visible)
                {
                    pbParseStatus.Value++;
                    pbParseStatus.Update();
                }


                //if (player != "") && !FindInKnownEnemiesList(player) && player.IndexOfAny(new char[] {'\'', ' '}, 0)==-1 )//for some reason still getting small amounts of damage with name' as the target
                {
                    TreeNode playerNode;
                    //we know the object exists and can be reference via a key, so
                    long individualAmount = (long)(htTotalDamagesByPlayer[player]);

                    if (individualAmount > 0)
                    {
                        string sText = EventHandlers.GetBar(player, individualAmount, totalDamageByAllPlayers);
                        if (damageRoot.Nodes.ContainsKey(player))
                        {
                            playerNode = damageRoot.Nodes[player];
                            if (playerNode.Text !=sText)
                                playerNode.Text = sText;
                            TagForMeterNodes tag =(TagForMeterNodes) playerNode.Tag;
                            tag.Amount = individualAmount;
                        }
                        else
                        {
                            playerNode = damageRoot.Nodes.Add(player, sText);
                            playerNode.Tag = new TagForMeterNodes(player, individualAmount);
                        }
                        
                    }
                }
                //else if (player == "")
                //{
                //    removeEmpty = true;
                //}
            }

            string damageTotalText = "Total: " + totalDamageByAllPlayers;

            if (damageRoot.Nodes.ContainsKey(subTotalKeyName))
                damageRoot.Nodes[subTotalKeyName].Text = damageTotalText;
            else
            {
                damagesTotalNode = damageRoot.Nodes.Add(subTotalKeyName, damageTotalText);
            }
            
            //if (removeEmpty)
            //    htTotalDamagesByPlayer.Remove(""); //shouldnt be there
            //removeEmpty = false;

            //Add a subtotal for heals

            /////////////////////////////////////////////////
            //NOW FOR HEALS...VERY SIMILAR TO DAMAGE CODE ABOVE
            //////////////////////////////////////////////////            
            if (pbParseStatus.Visible)
            {
                SimpleMessage("Populating tree view with " + htTotalHealsByPlayer.Count + " heals.");
                pbParseStatus.Maximum = htTotalHealsByPlayer.Count;
                pbParseStatus.Value = 0;
            }
            
            //show heals by each player
            foreach (string player in htTotalHealsByPlayer.Keys)
            {
                if (pbParseStatus.Visible)
                    pbParseStatus.Value++;

                bool isValidPlayerToAdd = player != "" && 
                    !FindInKnownEnemiesList(player) && !player.Contains("'") && 
                    player.IndexOfAny(new char[] {'\'', ' '}) == -1;

                //if (isValidPlayerToAdd)
                {
                    TreeNode playerNode;
                    long amount = Convert.ToInt64(htTotalHealsByPlayer[player]);

                    if (amount > 0)
                    {
                        string sText = EventHandlers.GetBar(player, amount, totalHealingByAllPlayers);
                        if (healingRoot.Nodes.ContainsKey(player))
                        {
                            playerNode = healingRoot.Nodes[player];
                            if (playerNode.Text != sText) 
                                playerNode.Text = sText;
                            TagForMeterNodes tag = (TagForMeterNodes) playerNode.Tag;
                            tag.Amount = amount;
                        }
                        else
                        {
                            playerNode = healingRoot.Nodes.Add(player, sText);
                            playerNode.Tag = new TagForMeterNodes(player, amount);
                        }
                       
                    }
                }
                //else if (player == "")
                //    removeEmpty = true;
            }
            string healingTotalText = "Total: " + totalHealingByAllPlayers;

            if (healingRoot.Nodes.ContainsKey(subTotalKeyName))
                healingRoot.Nodes[subTotalKeyName].Text = healingTotalText;
            else
            {
                healsTotalNode = healingRoot.Nodes.Add(subTotalKeyName, healingTotalText);
            }   
            //if (removeEmpty)
            //    htTotalHealsByPlayer.Remove(""); //shouldnt be there
            //removeEmpty = false;

            tvDamageAndHealing.Update();
            //SortDamageAndHealingIfNecessary();
        }

        private bool FindInKnownEnemiesList(string enemy)
        {
            return enemiesList.ContainsKey(enemy);
            //return FindInNodeNodes(enemy, tvEnemiesList.Nodes[0]);
         }

        private void UpdateThreatTreeView()
        {
            if (this.IsDisposed)
                return;

            if (calc.htGroupThreatFromLog == null || calc.htGroupThreatFromLog.Keys.Count == 0)
                return;

            if (pbParseStatus.Visible)
            {
                SimpleMessage("Populating tree view with " + calc.htGroupThreatFromLog.Count + " lines of threat.");

                pbParseStatus.Maximum = calc.htGroupThreatFromLog.Count;
                pbParseStatus.Value = 0;
            }

            foreach (string tar in calc.htGroupThreatFromLog.Keys)
            {
                if (pbParseStatus.Visible)
                {
                    pbParseStatus.Value = pbParseStatus.Value + 1;
                    pbParseStatus.Update();
                }


                Hashtable targetsHateTable = (Hashtable)calc.htGroupThreatFromLog[tar];

                if (FindInKnownPlayersList(tar) == false  &&
                    IsPet(tar) == false &&
                    tar!="You" &&
                    targetsHateTable.Count >= 1)
                {
                    //dont even bother adding if the count is 1, no one else has threat but some target on another target, big deal...dont add it.

                    totalThreatOnTarget = 0f; //reset here since we didnt create anew and initialize to zero.
                    //calculate total threat, and player with highest threat so we can normalize the threat bars
                    foreach (string player in targetsHateTable.Keys)
                    {
                        float threat = (float)targetsHateTable[player];
                        if (threat > maxThreatOfPlayers)
                            maxThreatOfPlayers = threat;
                        totalThreatOnTarget += (long)threat;
                    }
                    
                    if (targetsHateTable.Count ==0 )
                    {
                    }
                    //else if ( targetsHateTable.Count == 2)
                    //{
                    //   //only targets own self on himself, and one other person, dont show EITHER
                    //}
                    else if (totalThreatOnTarget > minTotalThreatTheshhold && targetsHateTable.Count > 2)
                    {
                        TreeNode tarNode;
                        string rootThreatAndName = tar;

                        if (tvLogFileThreat.Nodes.ContainsKey(tar))
                        {
                            tarNode = tvLogFileThreat.Nodes[tar];
                        }
                        else
                        {
                            tarNode = tvLogFileThreat.Nodes.Add(tar, rootThreatAndName);
                            tarNode.Text = rootThreatAndName;
                        }


                        if (totalThreatOnTarget >= startingBossHealth)
                        {
                            if (tarNode.ImageIndex != frmMeter.IconBoss)
                            {
                                tarNode.ImageIndex = frmMeter.IconBoss;
                            }
                            
                        }
                        else if (totalThreatOnTarget >= startingHighLevelMobHealth)
                        {
                            if (tarNode.ImageIndex != frmMeter.IconHighLevelMob)
                            {
                                tarNode.ImageIndex = frmMeter.IconHighLevelMob;
                            }
                            
                        }
                        else
                        {
                            if (tarNode.ImageIndex != frmMeter.IconLowLevelMob)
	                        {
                                tarNode.ImageIndex = frmMeter.IconLowLevelMob;
	                        }
                            
                            
                        }

                        foreach (string player in targetsHateTable.Keys)
                        {

                           float threat = (float)targetsHateTable[player];

                           if (threat > minThreatThresholdByPlayer)
                           {
                               string sNodeText = EventHandlers.GetBar(player, threat, maxThreatOfPlayers); //normalized bars
                               TreeNode PlayerNode = null;
                               if (tarNode.Nodes.ContainsKey(player))
                               {
                                   PlayerNode = tarNode.Nodes[player];

                                   if (!PlayerNode.TreeView.IsDisposed)
                                   {
                                       if (PlayerNode.Text != sNodeText)
                                           PlayerNode.Text = sNodeText;
                                       TagForMeterNodes t = (TagForMeterNodes)PlayerNode.Tag;
                                       t.Amount = (long)threat;
                                   }
                                   //PlayerNode.Tag = new TagForMeterNodes(player, (long)threat);
                               }
                               else //if (player != "")
                               {
                                   PlayerNode = tarNode.Nodes.Add(player, sNodeText);
                                   PlayerNode.ImageIndex = IconPlayer;//player icon
                                   PlayerNode.Tag = new TagForMeterNodes(player, (long)threat);
                               }
                           }

                        }
                        tarNode.Tag = new TagForMeterNodes(tar , (long)totalThreatOnTarget);

                        //add a total to each target or root node in the list
                        if (totalThreatOnTarget > minThreatThresholdForSubTotal 
                            && targetsHateTable.Count > 1) //dont put subtotal if less only 1 player in threattable
                        {
                            //string subTotalText = EventHandlers.GetBar("Total threat:", totalThreatOnTarget, totalThreatOnTarget);
                            string subTotalText = "Total:" +  totalThreatOnTarget;
                            TreeNode subTotalNode;
                            if (tarNode.Nodes.ContainsKey(subTotalKeyName))
                            {
                                subTotalNode = tarNode.Nodes[subTotalKeyName];
                                if (subTotalNode.Text != subTotalText)
                                    subTotalNode.Text = subTotalText;
                            }
                            else
                            {
                                subTotalNode = tarNode.Nodes.Add(subTotalKeyName, subTotalText);
                                subTotalNode.ImageIndex = IconCalculation;
                            }
                            subTotalNode.Tag = new TagForMeterNodes("Total Threat", long.MinValue); //make subtotal appear at bottom not top
                        }
                    }
                }
            }
            tvLogFileThreat.Update();
        }

        private bool IsPet(string rootTargetName)
        {
            int ioApostrophe = rootTargetName.IndexOf("'");
            if (ioApostrophe != -1)
            {
                //TreeNode knownPlayerListNode = tvPlayersList.Nodes[0];
                //return FindInNodeNodes(rootTargetName, knownPlayerListNode);
                return true;
            }
            else
                return false;
        }

        private bool FindInKnownPlayersList(string tar)
        {
            if (tar == "" || tar == null)
                return false;
            else
                return playersList.ContainsKey(tar);
            //TreeNode knownPlayerListNode = tvPlayersList.Nodes[0];
            //return FindInNodeNodes(tar, knownPlayerListNode);
        }

        private static bool FindInNodeNodes(string tar, TreeNode nodeWithNodes)
        {
            if (nodeWithNodes.Nodes.Count != 0)
            {
                //List<string> knownPlayersList = new List<string>();
                foreach (TreeNode n in nodeWithNodes.Nodes)
                {
                    if (n.Text == tar)
                        return true;
                }
                return false;
            }
            else
                return false;
        }

        private float lastThreatOnTarget = 0;

        //This is meant to reduce flickering by not resorting nodes every time
        //right now it currently does this every second.
        private void tmrSorter_Tick(object sender, EventArgs e)
        {        
          
            //UpdateThreatTreeView();
            //UpdateDamageAndHealingTreeView(ref calc.htTotalDamagesByPlayer, ref calc.htTotalHealsByPlayer);
            //UpdateTargetsTreeView();
            //SortIfNecessary();

            if (IsLogging)
            {
                UpdateNecessaryPages(false);
            }
        }
        public bool IsLogging
        {
            get
            {
                return tmrLogUpdater.Enabled;
            }
        }
        public void UpdateNecessaryPages()
        {
            UpdateNecessaryPages(false);
        }
        public void UpdateNecessaryPages(bool forceDrawOnAllPages)
        {
            //SortThreatIfNecessary();
            //SortDamageAndHealingIfNecessary();

            //try
            //{
            forceDrawOnAllPages = false; //overr

                if (forceDrawOnAllPages || tcAllPages.SelectedTab.Name == tpHealingGraph.Name)
                    DrawHeals();

                if (forceDrawOnAllPages || tcAllPages.SelectedTab.Name == tpDamageGraph.Name)
                    DrawDamages();

                if (forceDrawOnAllPages || tcAllPages.SelectedTab.Name == tpThreat.Name)
                    DrawThreat();

                if (forceDrawOnAllPages || tcAllPages.SelectedTab.Name == tpTargetting.Name)
                    UpdateTargetsTreeView();

                if (forceDrawOnAllPages || tcAllPages.SelectedTab.Name == tpThreatTree.Name)
                    UpdateThreatTreeView();

                if (forceDrawOnAllPages || tcAllPages.SelectedTab.Name == tpDamageAndHealingTree.Name
                    || tcAllPages.SelectedTab.Name == tpThreatTree.Name)
                    SortIfNecessary();
            //}
            //catch (Exception e)
            //{
            //    //probably closed form
            //}
        }

        public void DrawThreat()
        {
            DrawThreat(true);
        }

        private void DrawHeals(Graphics graphics, bool forceDraw)
        {
            DrawHeals(graphics, forceDraw, true);
        }

        private void DrawHeals(Graphics graphics)
        {
            DrawHeals(graphics, true, true);
        }

        private void SortIfNecessary()
        {
            SortThreatIfNecessary();
            SortDamageAndHealingIfNecessary();
        }

        private void SortThreatIfNecessary()
        {
            if (this.IsDisposed)
                return;

            if (totalThreatOnTarget != lastThreatOnTarget) //sort necessary
            {
                tvLogFileThreat.Sort();
                lastThreatOnTarget = totalThreatOnTarget;
            }
        }

        private void SortDamageAndHealingIfNecessary()
        {
            if (totalHealingByAllPlayers != lastHealingByAllPlayers
                || totalDamageByAllPlayers != lastDamageByAllPlayers)
            {
                tvDamageAndHealing.Sort();
                lastHealingByAllPlayers = totalHealingByAllPlayers;
                lastDamageByAllPlayers = totalDamageByAllPlayers;
            }
        }

        private void tsmiClearLogRaidThreatMeter_Click(object sender, EventArgs e)
        {
            this.btnClearAllThreatNow.Visible = true;
            Thread.CurrentThread.Join(5);
            this.btnClearAllThreatNow.Visible = false;
        }

        private void ClearRaidThreat()
        {
            tvLogFileThreat.Nodes.Clear();
            calc.htGroupThreatFromLog.Clear();
            cbHateFocusTarget.Items.Clear();
            lastThreatAmount = 0;
            cbHateFocusTarget.Text = "";
            this.htOfClientRect.Clear();
            SimpleMessage("Threat tables cleared!");
            DrawThreat(true);
        }
        private void toolTipsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            toolTipsToolStripMenuItem.Checked = !toolTipsToolStripMenuItem.Checked;
            ttMainTooltip.Active = toolTipsToolStripMenuItem.Checked;
            Properties.Settings.Default.ToolTips = ttMainTooltip.Active;
            Properties.Settings.Default.Save();
        }

        /// <summary>
        /// Automatically updates the log file when a new log file has been created.
        /// </summary>
        /// <param name="sender"></param>

        /// <param name="e"></param>
        long changeHits = 0;
        private void fswLogChanged_Created(object sender, FileSystemEventArgs e)
        {
            if (changeHits++ == 0)
                return;

            InfoLogIt("New combat log file " + e.Name + " has been detected. Please update your log file using the program menu option: \"" + tsmiSetupLogWizard.Text.Replace("&", "") + "\"", null,
                "New log detected.");

            if (!IsLogging)
            {
                int idxOfLastSlash = e.FullPath.LastIndexOf("\\");
                if (e.FullPath.Substring(idxOfLastSlash, e.FullPath.Length - idxOfLastSlash).Contains("CombatLog"))
                {
                    SimpleMessage("New log detected at: " + e.FullPath);
                    //openFileDialog1.FileName = e.FullPath;
                    //OpenSpecifiedFile();
                    ////DialogResult drUpdate = System.Windows.Forms.MessageBox.Show(e.FullPath + @" has been created. If you ran \logcombat off and \logcombat on you will need to update the log file location to this file.", "Update Log File?", MessageBoxButtons.YesNo);
                    ////if (drUpdate == System.Windows.Forms.DialogResult.Yes)
                    ////{
                    ////    openFileDialog1.FileName = e.FullPath;
                    ////    OpenAndStartLogging(false, e.FullPath);
                    ////}
                }
                else
                {
                    Debug.Assert(true);
                }
            }
        }
  
        private void cbLogging_Click(object sender, EventArgs e)
        {
            bool userSaysEnable = cbLogging.Checked;
            TurnTimersEnabled(userSaysEnable);
            
        }

        private void tscbPlayerName_KeyDown(object sender, KeyEventArgs e)
        {
             if (e.KeyCode == Keys.Enter)
            {
                ParsePlayerNameInput();
                AddKnownPlayer(playerName, true);
            }
        }
        private bool ParsePlayerNameInput()
        {
            string playerNameContents = tscbPlayersPlayed.Text;
            playerNameContents = playerNameContents.Replace("Name:", "").Trim();

            return ChangePlayerName(playerNameContents);
        }

        //should update your name in all data
        private bool ChangePlayerName(string newPlayerName)
        {
            if (newPlayerName == null || newPlayerName == "" || newPlayerName.Trim().Contains(" "))
                return false;

            playerName = newPlayerName.Trim();
            if (!tscbPlayersPlayed.Items.Contains(newPlayerName))
            {
                tscbPlayersPlayed.Items.Add(newPlayerName);
            }
            //save list
            SavePlayersPlayedToFile();
            return true;
        }    
        private void SavePlayersPlayedToFile()
        {
            List<string> playersPlayedList = new List<string>();
            foreach (object o in tscbPlayersPlayed.Items)
            {
                playersPlayedList.Add(o as string);
            }
            //if (playersPlayedList.Count == 0)
            //   File.Delete(playersPlayedFile);
            //else
                SaveToXMLFile(playersPlayedList, playersPlayedFile);
        }


        private void SavePlayersToFile()
        {
            //SortedList<string, Player> sortedList = playersList;
            //String file = playerFile;
            //Player[] allPlayers = new Player[sortedList.Count];
            //int idx = 0;
            //foreach (string key in sortedList.Keys)
            //    allPlayers[idx++]=sortedList[key];

            //SaveToBinaryFile(allPlayers, file);
            PlayersListEditorControl.SaveToBinaryFile(playerFile);
        }

        private bool LoadPlayersFromFile()
        {
            try{
                PlayersListEditorControl.LoadFromBinaryFile(playerFile, true);
                if (playersList == null || playersList.Count == 0)
                {
                    Player you = new Player("You");
                    you.GraphColor = Color.Green;
                    playersList.Add("You", you); //have at least yourself always in there, so ou can configure graph colors without havving to add yourself.
                }
                //var playersList = dic1.ToDictionary(kvp => kvp.Key as string, kvp => kvp.Value as Player);
                //playersList = PlayersListEditorControl.LoadFromBinaryFile(playerFile, true).ToDictionary<string,Player>();
                return true;
            }
            catch (Exception e)
            {
                return false;
            }
        }
            //String file = playerFile;

            //Player[] aPlayers = null;
            //aPlayers = (Player[])LoadFromBinaryFile(file);

            //playersList = new SortedList<string, Player>();
            //if (aPlayers == null){
            //    return;
            //}
            //else
            //{
            //    TreeNode knownPlayerListNode = tvPlayersList.Nodes[0];
            //    knownPlayerListNode.Nodes.Clear();
            //    foreach (Player p in aPlayers)
            //    {
            //        playersList.Add(p.Name, p);
            //        TreeNode playerNode = new TreeNode(p.Name);
            //        //Player thePlayer = new Player(p.Name, false, playerNode);
            //        playerNode.Tag = p;
            //        if (p.Name == playerName)
            //            playerNode.ImageIndex = IconYourself;
            //        else
            //            playerNode.ImageIndex = IconPlayer;//player icon

            //        knownPlayerListNode.Nodes.Add(playerNode);

            //    }

            //    tvPlayersList.Update();
            //}
        //}
        public static object LoadFromBinaryFile(string file)
        {

            try
            {
                // Create a new file stream for reading the XML file
                if (File.Exists(file))
                {
                    FileStream ReadFileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read);
                    // Create a new XmlSerializer instance with the type of the test class
                    BinaryFormatter bf = new BinaryFormatter();
                    //XmlSerializer SerializerObj = new XmlSerializer(typeof(List<string>));

                    // Load the object saved above by using the Deserialize function
                    object objLoaded = bf.Deserialize(ReadFileStream);//SerializerObj.Deserialize(ReadFileStream);

                    // Cleanup
                    ReadFileStream.Close();
                    return objLoaded;
                }
                else return null;

            }
            catch (Exception e)
            {
                return null;
            }
        }
        /// <summary>
        ///      http://www.jonasjohn.de/snippets/csharp/xmlserializer-example.htm
        /// </summary>
        /// <param name="objToSave"></param>
        /// <param name="fileName"></param>
        public static bool SaveToXMLFile(object objToSave, string fileName)
        {

            if (objToSave == null)
                return false;

            TextWriter WriteFileStream = null;
            try
            {
                // Create a new XmlSerializer instance with the type of the test class
                XmlSerializer SerializerObj = new XmlSerializer(objToSave.GetType());

                // Create a new file stream to write the serialized object to a file
                WriteFileStream = new StreamWriter(fileName, false);
                SerializerObj.Serialize(WriteFileStream, objToSave);
                WriteFileStream.Flush();
                // Cleanup

                return true;
            }
            catch (Exception ioException)
            {
                return false;
            }
            finally
            {
               if (WriteFileStream!=null)
                   WriteFileStream.Close();
            }              
        }

        public static bool SaveToBinaryFile(object objToSave, string fileName)
        {

            if (objToSave == null)
                return false;

            FileStream WriteFileStream = null;
            try
            {
                // Create a new XmlSerializer instance with the type of the test class
                BinaryFormatter SerializerObj = new BinaryFormatter();// XmlSerializer(objToSave.GetType());

                // Create a new file stream to write the serialized object to a file
                WriteFileStream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write);
                SerializerObj.Serialize(WriteFileStream, objToSave);
                //SerializerObj.Serialize(WriteFileStream, objToSave);
                WriteFileStream.Flush();
                // Cleanup

                return true;
            }
            catch (Exception ioException)
            {
                return false;
            }
            finally
            {
                if (WriteFileStream != null)
                    WriteFileStream.Close();
            }
        }

        //http://www.jonasjohn.de/snippets/csharp/xmlserializer-example.htm
        private void LoadPlayersPlayedFromFile()
        {
            tscbPlayersPlayed.Items.Clear();

            List<string> playersPlayedList = (List<string>) LoadFromXMLFile(playersPlayedFile);
            if (playersPlayedList != null)
            foreach (string playerPlayed in playersPlayedList)
            {
                tscbPlayersPlayed.Items.Add(playerPlayed);
            }
            //playerName = tscbPlayersPlayed.Items[0] as string;
        }
        
        /*
        string knownPlayersFile = "known-players.xml";
        private void LoadKnownPlayersFromFile()
        {
        }
        */
        string KOSListFile = "kos.dat";

        private void LoadKOSListFromFile()
        {
            KOSPlayerInfo[] all = (KOSPlayerInfo[])LoadFromBinaryFile(KOSListFile);

            kosList = new Dictionary<string, KOSPlayerInfo>();

            if (all == null)
                return;
            else
            {
                foreach (KOSPlayerInfo kosPlayer in all)
                {
                    kosList.Add(kosPlayer.PlayerName, kosPlayer);
                    bool isChecked = true;
                    clbKOSList.Items.Add(kosPlayer, isChecked);
                }
            }

        }

        private bool SaveKnownEnemiesToFile()
        {
            return this.EnemiesListEditorControl.SaveToBinaryFile(knownEnemiesFile);
        }

        public static object LoadFromXMLFile(string file)
        {

            try
            {
                // Create a new file stream for reading the XML file
                if (File.Exists(file))
                {
                    FileStream ReadFileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read);
                    // Create a new XmlSerializer instance with the type of the test class
                    XmlSerializer SerializerObj = new XmlSerializer(typeof(List<string>));
    
                    // Load the object saved above by using the Deserialize function
                    object objLoaded = SerializerObj.Deserialize(ReadFileStream);
        
                    // Cleanup
                    ReadFileStream.Close();
                    return objLoaded;
                } else return null;

            }
            catch (Exception e) {
                return null;
            }
        }

        private void tscbPlayerName_SelectedIndexChanged(object sender, EventArgs e)
        {
            ChangePlayerName(tscbPlayersPlayed.Text);
        }

        private void tsmiClearCheckBoxes_Click(object sender, EventArgs e)
        {
            CheckAll(false);
        }

        private void CheckAll(bool checkValue)
        {
            foreach (TreeNode rootNode in tvLogFileThreat.Nodes)
            {
                rootNode.Checked = checkValue;
                foreach (TreeNode subNode in rootNode.Nodes)
                {
                    subNode.Checked = checkValue;
                }
            }
        }

        //private void tsmiRemoveCheckedFromKnownPlayers_Click(object sender, EventArgs e)
        //{
        //    TreeNode knownPlayersNode = tvPlayersList.Nodes[0];

        //    if (tvLogFileThreat.Nodes.Count != 0)
        //    {
        //        List<TreeNode> playerNodesToRemove = new List<TreeNode>();
        //        foreach (TreeNode rootNode in tvLogFileThreat.Nodes)
        //        {
        //            if (rootNode != null && rootNode.Checked)
        //            {
        //                string nodeText = rootNode.Text;
        //                TreeNode kpNode = knownPlayersNode.Nodes.Add(nodeText, nodeText);
        //                Player pNewPlayerMovingIn = new Player(nodeText, false, kpNode);
        //                if (nodeText == playerName)
        //                    kpNode.ImageIndex = IconYourself;//player icon
        //                else
        //                    kpNode.ImageIndex = IconPlayer;
        //                playerNodesToRemove.Add(rootNode);
        //            }
        //        }
        //        foreach (TreeNode nodeToRemove in playerNodesToRemove)
        //        {
        //            if (nodeToRemove != null)
        //            {
        //                tvPlayersList.Nodes.Remove(nodeToRemove);
        //            }
        //        }
        //    }
        //    else
        //    {
        //        System.Windows.Forms.MessageBox.Show("There are no items in the threat view! Open a combat log file to populate it.");
        //    }
        //}

        public static string ProperCasePlayerName(string player)
        {
            //CapitalizeName
            string knownPlayerToAdd = player.Trim().ToLower();
            if (knownPlayerToAdd == "") { return ""; }
            else if (knownPlayerToAdd.Length == 1)
                knownPlayerToAdd = knownPlayerToAdd[0].ToString().ToUpper();
            else
                knownPlayerToAdd = knownPlayerToAdd[0].ToString().ToUpper() + knownPlayerToAdd.Substring(1, knownPlayerToAdd.Length - 1);
            return knownPlayerToAdd;
        }



        private void tsmiBorderless_Click(object sender, EventArgs e)
        {
            Borderless(!tsmiBorderless.Checked);
        }

        private void Borderless(bool borderLess)
        {
            tsmiBorderless.Checked = borderLess;

            if (!borderLess)
            {
                this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Sizable;
                this.ShowIcon = true;
                this.ControlBox = true;
                btnFormClose.Visible = false; //hide when control box is up
                btnMinimizeForm.Visible = false;
                btnSizeBRCorner.Visible = false;
            }
            else
            {
                this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
                this.ShowIcon = false;
                this.ControlBox = false;
                btnFormClose.Visible = true;
                btnMinimizeForm.Visible = true;
                btnSizeBRCorner.Visible = true;
            }
        }

        private void toolStripMain_MouseDown(object sender, MouseEventArgs e)
        {
            startMousePosition = e.Location;
            toolStripMain.Capture = true;
        }

        private void toolStripMain_MouseMove(object sender, MouseEventArgs e)
        {
            if (toolStripMain.Capture)
            {
                Point cp = e.Location;
                int dx = 0, dy = 0;
                dx = cp.X - startMousePosition.X;
                dy = cp.Y - startMousePosition.Y;
                Point newFormLocation = Point.Empty;

                newFormLocation.X = this.Location.X + dx;
                newFormLocation.Y = this.Location.Y + dy;
                this.Location = newFormLocation;
            }
        }

        private void toolStripMain_MouseUp(object sender, MouseEventArgs e)
        {
            toolStripMain.Capture = false;
        }

        private void btnFormClose_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void tsddbCommands_MouseHover(object sender, EventArgs e)
        {
            //tsddbCommands.BackColor = Color.GhostWhite;
            tsddbCommands.ForeColor = Color.Yellow;
        }

        private void tsddbCommands_MouseLeave(object sender, EventArgs e)
        {
            //tsddbCommands.BackColor = Color.Transparent;
            tsddbCommands.ForeColor = Color.SteelBlue;
        }

        private void btnSizeBRCorner_MouseDown(object sender, MouseEventArgs e)
        {
            btnSizeBRCorner.Capture = true;
            startMousePosFormSizer = e.Location;
        }

        private void btnSizeBRCorner_MouseUp(object sender, MouseEventArgs e)
        {
            btnSizeBRCorner.Capture = false;
            startMousePosFormSizer = Point.Empty;
        }


        private void btnSizeBRCorner_MouseMove(object sender, MouseEventArgs e)
        {
            if (btnSizeBRCorner.Capture)
            {

                Point dp = new Point(startMousePosFormSizer.X - e.Location.X, startMousePosFormSizer.Y - e.Location.Y);
                Size nsz = new Size(this.Size.Width - dp.X, this.Size.Height - dp.Y);
                this.Size = nsz;
            }
        }

        private void btnSizeBRCorner_MouseHover(object sender, EventArgs e)
        {
            btnSizeBRCorner.ForeColor = Color.Yellow;
        }

        private void btnSizeBRCorner_MouseLeave(object sender, EventArgs e)
        {
            btnSizeBRCorner.ForeColor = Color.Black;
        }

        private void btnMinimizeForm_Click(object sender, EventArgs e)
        {
            this.WindowState = FormWindowState.Minimized;
        }

        private void toolStripMain_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            Borderless(!tsmiBorderless.Checked);
        }

        private void tsmiUseTransparentKey_Click(object sender, EventArgs e)
        {
            tsmiUseTransparentKey.Checked = !tsmiUseTransparentKey.Checked;
            if (tsmiUseTransparentKey.Checked == false)
                this.TransparencyKey = Color.Empty;
            else
                this.TransparencyKey = Color.White;// should have custom color here...background images maybe too.
        }

        
        private void frmMeter_MouseDown(object sender, MouseEventArgs e)
        {
            startMousePosition = e.Location;
            this.Capture = true;
        }

        private void frmMeter_MouseUp(object sender, MouseEventArgs e)
        {
            this.Capture = false;
            startMousePosFormSizer = Point.Empty;
        }

        private void frmMeter_MouseMove(object sender, MouseEventArgs e)
        {
            if (this.Capture)
            {
                Point cp = e.Location;
                int dx = 0, dy = 0;
                dx = cp.X - startMousePosition.X;
                dy = cp.Y - startMousePosition.Y;
                Point newFormLocation = Point.Empty;

                newFormLocation.X = this.Location.X + dx;
                newFormLocation.Y = this.Location.Y + dy;
                this.Location = newFormLocation;
            }
        }

        //private void tsmiEnableCheckBoxes_Click(object sender, EventArgs e)
        //{
        //    tvLogFileThreat.CheckBoxes = !tvLogFileThreat.CheckBoxes;
        //    tvDamageAndHealing.CheckBoxes = tvLogFileThreat.CheckBoxes; //do after above, to sync them.
        //}


        //public bool AddKnownPlayer(string playerName, bool treeViewUpdates)
        //{
            
        //    if (playerName == null || playerName == "" || playerName == subTotalKeyName || playerName.Contains(" "))
        //    {
        //        Debug.WriteLine("Cannot add " + playerName + " to known players.");
        //        return false;
        //    }

        //    string knownPlayerToAdd = ProperCasePlayerName(playerName.Trim());

        //    ////TreeNode knownPlayerListNode = tvPlayersList.Nodes[0];
        //    //////could look through sorted list for optimizations:.........
        //    //////could look through sorted list for optimizations:.........
        //    //////could look through sorted list for optimizations:.........
        //    ////if (FindInKnownPlayersList(knownPlayerToAdd) == false)
        //    ////{
        //    ////    TreeNode knownPlayer =null;
        //    ////    if (treeViewUpdates)
        //    ////    {
        //    ////        knownPlayer = knownPlayerListNode.Nodes.Add(knownPlayerToAdd, knownPlayerToAdd);
        //    ////        if (knownPlayerToAdd != playerName)
        //    ////            knownPlayer.ImageIndex = IconPlayer;//player icon
        //    ////        else
        //    ////            knownPlayer.ImageIndex = IconYourself;
        //    ////    }

        //    ////    Player playerData = new Player(knownPlayerToAdd, false, knownPlayer);
        //    ////    //dont forget to add the new player
        //    ////    playersList.Add(knownPlayerToAdd, playerData);
        //    ////}

        //    calc.htGroupThreatFromLog.Remove(knownPlayerToAdd);
        //    ////if (treeViewUpdates)
        //    ////{
        //    ////    if (tvLogFileThreat.Nodes.Count !=0 && tvLogFileThreat.Nodes[0].Nodes.ContainsKey(knownPlayerToAdd))
        //    ////       tvLogFileThreat.Nodes[0].Nodes.RemoveByKey(knownPlayerToAdd);
        //    ////}
        //    return true;
        //}

        private void btnCancel_Click(object sender, EventArgs e)
        {
            cancelClicked = true;
        //            private void CancelLoadFile(ref Hashtable htTotalDamagesByPlayer, ref Hashtable htTotalHealsByPlayer)
        //{
            //do clean up                    
            calc.htTotalDamagesByPlayer.Clear();
            calc.htTotalHealsByPlayer.Clear();
            calc.htGroupThreatFromLog.Clear();

            damageRoot.Nodes.Clear();
            healingRoot.Nodes.Clear();
            tvLogFileThreat.Nodes.Clear();

            cancelClicked = false;
            return;
         }

        private void tsmiClearDamageAndHealingMeter_Click(object sender, EventArgs e)
        {
            calc.htTotalDamagesByPlayer.Clear();
            calc.htTotalHealsByPlayer.Clear();

            damageRoot.Nodes.Clear();
            healingRoot.Nodes.Clear();

            DrawDamages();
            DrawHeals();
        }

        //private void tsmiAddCheckedToKnownPlayers_Click(object sender, EventArgs e)
        //{
        //    List<TreeNode> toRemove = new List<TreeNode>();
        //    foreach (TreeNode n in tvDamageAndHealing.Nodes)
        //    {
        //        if (n!= null && n.Checked)
        //        {
        //            AddKnownPlayer(n.Text, false);
        //            toRemove.Add(n);
        //        }
        //    }
        //    //now delete all in the delete list
        //    foreach (TreeNode ntoDelete in toRemove)
        //    {
        //        tvDamageAndHealing.Nodes.Remove(ntoDelete);
        //    }
        //}

        private void tvDamageAndHealing_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
                cmsDandHTreeCommands.Show(tvDamageAndHealing, e.Location);
            UpdateDamageAndHealingTreeView();
        }

        private void UpdateDamageAndHealingTreeView()
        {
            UpdateDamageAndHealingTreeView(ref calc.htTotalDamagesByPlayer, ref calc.htTotalHealsByPlayer);
        }

        public Enemy AddKnownEnemy(string enemyToAdd, bool remove, bool updateControl)
        {
            Enemy enemyAdded = null;

            if (!enemiesList.ContainsKey(enemyToAdd))
            {
                enemyAdded = new Enemy(enemyToAdd);
                enemiesList.Add(enemyToAdd, enemyAdded);
                if (updateControl)
                    EnemiesListEditorControl.RefreshItems();
            }
            return null;
        }
        private void tsmiMoveCheckedToKnownEnemies_Click(object sender, EventArgs e)
        {
            List<TreeNode> nodesToRemove = new List<TreeNode>();
            //damage nodes
            foreach (TreeNode damNode1 in damageRoot.Nodes)
            {
                if (damNode1.Checked && damNode1!= damagesTotalNode)
                {
                    string enemy = GetNameByNode(damNode1);
                    if (AddKnownEnemy(enemy, false, false) != null)
                        nodesToRemove.Add(damNode1);
                }
            }

            //healing nodes
            foreach (TreeNode healNode1 in healingRoot.Nodes)
            {
                if (healNode1.Checked && healNode1 !=healsTotalNode)
                {
                    string enemy = GetNameByNode(healNode1);
                    if (AddKnownEnemy(enemy, false, false)!=null)
                        nodesToRemove.Add(healNode1);
                }
            }
            //remove both damage and healing nodes if they were added to enemy list
            foreach (TreeNode nToDelete in nodesToRemove)
            {
                if (nToDelete != null && nToDelete.Checked)
                {
                    tvDamageAndHealing.Nodes.Remove(nToDelete);
                }
            }
            EnemiesListEditorControl.RefreshItems();
        }

        private string GetNameByNode(TreeNode damNode1)
        {
            TagForMeterNodes tag = (TagForMeterNodes) damNode1.Tag;
            if (tag != null)
                return tag.Target;
            else
                return damNode1.Text;
        }




        private void tvLogFileThreat_NodeMouseHover(object sender, TreeNodeMouseHoverEventArgs e)
        {
            TagForMeterNodes tag = (TagForMeterNodes)e.Node.Tag;
            if (tag != null)
                e.Node.ToolTipText = tag.Target + " => " + tag.Amount;
        }

        private void tvDamageAndHealing_NodeMouseHover(object sender, TreeNodeMouseHoverEventArgs e)
        {
            TagForMeterNodes tag = (TagForMeterNodes)e.Node.Tag;
            if (tag != null)
                e.Node.ToolTipText = tag.Target + " => " + tag.Amount;
        }

        private void tsbMakeReport_Click(object sender, EventArgs e)
        {
            string gamePath = GetGamePath();
            ScriptReporter reporter = new ScriptReporter(gamePath, playerName, ref calc);
            reporter.Show();
        }

        private string GetGamePath()
        {
            string gamePath = openFileDialog1.FileName.Substring(0, openFileDialog1.FileName.LastIndexOf("\\") + 1);
            return gamePath;
        }


        private void tsmiCheckAllThreatTargets_Click(object sender, EventArgs e)
        {
            foreach (TreeNode n in tvLogFileThreat.Nodes)
            {
                n.Checked = true;
            }
        }

        private void tsmiUncheckAllThreatTargets_Click(object sender, EventArgs e)
        {
            foreach (TreeNode n in tvLogFileThreat.Nodes)
            {
                n.Checked = false;
            }
        }


        private void pbDamage_Paint(object sender, PaintEventArgs e)
        {
            DrawDamages(e.Graphics, true);
        }
        public void DrawDamages()
        {
            if (pbDamage.IsDisposed)
            {
                return;
            }
            Graphics damageGraph = pbDamage.CreateGraphics();
            DrawDamages(damageGraph, true);
            damageGraph.Flush();
        }
        private void DrawDamages(Graphics g, bool redrawAll)
        {
            List<Stat> playersByAmount = calc.GetPlayersSortedByDamage();

            //e.ClipRectangle
            int maxBarWidth = pbDamage.ClientSize.Width;

            g.FillRectangle(fillBgBrush, 0, 0, maxBarWidth, pbDamage.ClientSize.Height);

            DamageFillBarBrush = new LinearGradientBrush(new Point(0, 0), new Point(pbDamage.ClientSize.Width, pbDamage.ClientSize.Height), Color.WhiteSmoke, Color.Blue );
            dBars.Clear();

            if (playersByAmount != null)
            {

                long totalAmount = calc.GetTotalDamages();
                string maxedPlayer = null;
                long maxAmountByPlayer = calc.GetMaxDamagePlayer(out maxedPlayer, (List <Stat>) playersByAmount);

                DrawDamageStats(g, playersByAmount,
                    GraphTypes.Damage, true,
                    maxBarWidth, totalAmount, maxAmountByPlayer);
            }

           g.Flush();
            //pbDamage.Update();
        }

        private void DrawDamageStats(Graphics g, List<Stat> playersByAmount,
            GraphTypes barGraphType, bool drawFontOnBar,
            int maxBarWidth, long totalAmount, long maxAmountByPlayer)
        {

            int playerCount = playersByAmount.Count;
            int barHeight = (int)(pbDamage.ClientSize.Height / (float)playerCount);
            const int maxFontHeightPixels = 12;

            int fontHeightPixels = barHeight > maxFontHeightPixels ? maxFontHeightPixels : barHeight;
            if (fontHeightPixels < 3)
            {
                fontHeightPixels = 3;
            }
            Font drawPlayerFont = new Font(FontFamily.GenericSerif, fontHeightPixels, System.Drawing.GraphicsUnit.Pixel);

            if (!(totalAmount == 0 || playerCount == 0))
            {

                int barLeft = 0;
                int rank = 0;
                foreach (Stat stPlayer in playersByAmount)
                {
                    int barTop = (int)(rank * barHeight);
                    long amount = stPlayer.Damage;
                    Rectangle client = new Rectangle(0, 0, pbDamage.Width, pbDamage.Height);
                    //if (stPlayer.Target == "You")
                    //    DrawNormalizedBar(g, YourBrushForBarFilling, YourPenForBarBorder , amount, client, barHeight, maxAmountByPlayer,  rank, stPlayer.Target, null);
                    //else
                    Brush barBrush = this.GetPlayerBrush(stPlayer.Target, GraphTypes.Damage);
                    if (barBrush == null)
                        barBrush = DamageFillBarBrush;
                    Pen barPen = this.GetPlayerPen(stPlayer.Target, GraphTypes.Damage);
                    if (barPen == null)
                        barPen = DamageBarPen;

                    DrawNormalizedBar(g, barBrush, barPen, amount, client, 0 , barHeight, maxAmountByPlayer, rank, stPlayer.Target, null, 0, null);
                    //if (drawFontOnBar)
                    //    g.DrawString(stPlayer.Target, drawPlayerFont, Brushes.Black, new Point(0, barTop));

                    rank++;
                }
                DrawHorizontalAxis(g, new Rectangle(0, 0, pbDamage.ClientRectangle.Width, pbDamage.ClientRectangle.Height), maxAmountByPlayer, true);
                //g.Flush();
            }
        }

        public string TargetOfFocus()
        {
            string focus = FocusTarget;
            return TargetOf(focus);
            //foreach (string key in targetting.Keys)
            //{
            //    List<ParsedLogEvent> targetsList = targetting[key];

            //    if (targetsList.Count > 0)
            //    {
            //        t                   
            //    }
            //}
        }
        public string TargetOf(string target)
        {
            if (target == null)
                return target;

            Dictionary<string, List<ParsedLogEvent>> targetting = this.dDamageEventsOnTargets;
            if (targetting == null)
            {
                return null;
            }
            else if (targetting.ContainsKey(target))
            {
                List<ParsedLogEvent> lDamageEvents = targetting[target];
                if (lDamageEvents.Count > 0)
                {
                    string lastTargetInList = lDamageEvents[lDamageEvents.Count - 1].Target;
                    return lastTargetInList;
                }
                else return null;
            }
            else return null;

        }
        public void DrawHeals()
        {
            if (pbHealing.IsDisposed)
            {
                return;
            }

            DrawHeals(pbHealing.CreateGraphics(), true);
        }

        long lastAmountCount = 0;
        private void DrawHeals(Graphics g, bool forceRepaint, bool drawPlayerNameOnBars)
        {
            const int maxFontHeightPixels = 12;

            List<Stat> playersByAmount = calc.GetPlayersSortedByHeals();

            long amountCount = GetEventAmountCountOnHealing(playersByAmount);

            if (amountCount == 0 || amountCount == lastAmountCount)
            {
                lastAmountCount = amountCount;
            }
            if (forceRepaint || amountCount != lastAmountCount)
            {
                lastAmountCount = amountCount;
                //e.ClipRectangle
                int maxBarWidth = pbHealing.ClientSize.Width;

                g.FillRectangle(fillBgBrush, 0, 0, maxBarWidth, pbHealing.ClientSize.Height);

                HealingFillBarBrush = new LinearGradientBrush(new Point(0, 0), new Point(pbHealing.ClientSize.Width, pbHealing.ClientSize.Height), Color.WhiteSmoke, Color.LightGreen);
                dBars.Clear();

                if (playersByAmount != null && playersByAmount.Count != 0)
                {

                    int playerCount = playersByAmount.Count;

                    int barHeight = (int)(pbHealing.ClientSize.Height / (float)playerCount);

                    long totalAmount = calc.GetTotalHealing();

                    string maxedPlayer = null;
                    long maxAmountByPlayer = calc.GetMaxHealingByPlayer(out maxedPlayer);

                    int fontHeighPixels = barHeight > maxFontHeightPixels ? maxFontHeightPixels : barHeight;

                    Font drawPlayerFont = new Font(FontFamily.GenericSerif, fontHeighPixels, System.Drawing.GraphicsUnit.Pixel);

                    if (!(totalAmount == 0 || playerCount == 0))
                    {

                        int rank = 0;
                        Rectangle client = new Rectangle(0, 0, pbHealing.ClientSize.Width, pbHealing.ClientSize.Height);
                        foreach (Stat stPlayer in playersByAmount)
                        {
                            int barTop = (int)(rank * barHeight);
                            long amount = stPlayer.Healing;

                            Brush barBrush = this.GetPlayerBrush(stPlayer.Target, GraphTypes.Healing);
                            Pen barPen = this.GetPlayerPen(stPlayer.Target, GraphTypes.Healing);

                            //if (stPlayer.Target == "You")
                            //    DrawNormalizedBar(g, YourBrushForBarFilling, YourPenForBarBorder, amount, client, barHeight, maxAmountByPlayer, rank, stPlayer.Target, null);
                            //else
                            DrawNormalizedBar(g, HealingFillBarBrush, HealingBarPen, amount, client, 0, barHeight, maxAmountByPlayer, rank, stPlayer.Target, null, 0, null);

                            //if (drawPlayerNameOnBars)
                            //{
                            //    g.DrawString(stPlayer.Target, drawPlayerFont, Brushes.Black, new Point(0, barTop));
                            //}
                            rank++;
                        }

                        DrawHorizontalAxis(g, new Rectangle(0, 0, pbHealing.Width, pbHealing.Height), maxAmountByPlayer, true);
                    }
                }
                g.Flush();
            }
        }

        private long GetEventAmountCountOnHealing(List<Stat> playersByAmount)
        {
            long amCount = 0;
            if (playersByAmount !=null)
            foreach(Stat st in playersByAmount)
            {
                amCount += st.Healing;  
            }
            return amCount;
        }

        public bool IsPossibleEnemy(string targetPlayerOrEnemy)
        {
            if (targetPlayerOrEnemy == null || targetPlayerOrEnemy == "" || targetPlayerOrEnemy == "You")
                return false;

            bool targetContSp = targetPlayerOrEnemy.Contains(" ");
            if (targetContSp) return true;

            bool targetContDash = targetPlayerOrEnemy.Contains("-");
            if (targetContDash) return true;

            bool isKnownEnemy = enemiesList!=null && enemiesList.ContainsKey(targetPlayerOrEnemy);
            bool isKnownPlayer = playersList!=null && playersList.ContainsKey(targetPlayerOrEnemy);
            if ( isKnownEnemy && !isKnownPlayer)
            {
                return true;
            }
            else
                return false;
            //if (htGroupThreatFromLog.ContainsKey(targetPlayerOrEnemy))
            //    return true;
            //else
            //    return false;
        }
        private void DrawHorizontalAxis(Graphics g, Rectangle clientRectangle, long maxAmountByPlayer, bool drawAxisTickerLabels)
        {
            int axisWidth = clientRectangle.Width, axisBottom = clientRectangle.Height;

            //int numDigits = maxAmountByPlayer.ToString().Length; //how many digits in the decimal number
            long axisLength = maxAmountByPlayer;//(long)(Math.Pow(10,numDigits));
            const int tickCount = 10;
            long axisDivisionLength = axisLength / tickCount;

            //long axisTickLength = axisWidth / 10;
            int tickBottom = axisBottom;
            Font tickerFont = new Font(FontFamily.GenericSerif, 8);//, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Pixel);



            for (int tick = 1; tick < tickCount; tick++)
            {
                int tickLeft = (int)(tick / (float)tickCount * axisWidth);
                int tickTop = tickBottom - 5;
                Point tickSpotBL = new Point(clientRectangle.Left + tickLeft, clientRectangle.Top + tickBottom);
                Point tickSpotTL = new Point(clientRectangle.Left + tickLeft, clientRectangle.Top + tickTop);

                g.DrawLine(Pens.Gray, tickSpotBL, tickSpotTL);

                if (drawAxisTickerLabels)
                {
                    float tickAmount = (float)axisLength * tick / tickCount; //dont show any digits past 1000's place

                    StringFormat sf = new System.Drawing.StringFormat();

                    sf.FormatFlags = System.Drawing.StringFormatFlags.DirectionVertical;// | System.Drawing.StringFormatFlags.DirectionRightToLeft;
                    PointF stringSpot = new PointF(clientRectangle.Left -tickLeft, clientRectangle.Top -tickBottom); //since rotated switch ordinates ?
                    //g.TranslateTransform(0, axisBottom);
                    g.RotateTransform(180);

                    string sTickAmount = null;
                    if (axisDivisionLength <= 1000)
                        sTickAmount = ((long)tickAmount).ToString();//("#,#.#" + "K");
                    else if (axisDivisionLength <= 10000)
                        sTickAmount = (tickAmount/1000f).ToString("#,#.#" + "K");
                    else
                        sTickAmount = (tickAmount / 1000f).ToString("#,#" + "K");

                    g.DrawString(sTickAmount, tickerFont, Brushes.Black, stringSpot, sf);
                    g.ResetTransform();
                }
            }
        
        }
        public class EnemyPlayerPair
        {
            public string Enemy;
            public string Player;
            public EnemyPlayerPair(string enemy, string player)
            {
                this.Enemy = enemy;
                this.Player = player;
            }
        }

        Pen tankingBorderPen = new Pen(Color.Yellow, 2);
        Brush BrushForTakingDamage = new HatchBrush(HatchStyle.DottedGrid, Color.Red);

        private void DrawNormalizedBar(Graphics g, Brush fillBarBrush, Pen barBorderPen, 
            long amount, Rectangle client, int captionHeight, int barHeight, 
            long maxAmountByPlayer, int rank, string player, string symbol, int focusStatus, string enemy)
        {
            Brush PlayerFontBrush = Brushes.Black;

            if (maxAmountByPlayer == 0)
                return;
            if (symbol == null) symbol = "";

            float normalizedAmount = (float)(amount / (float)maxAmountByPlayer);
            int barWidth = (int)((float)normalizedAmount * client.Width);
            int barTop = (int)(rank * barHeight) + captionHeight;
            Rectangle theBar = new Rectangle(client.Left, client.Top + barTop, barWidth, barHeight);


            const bool overrideBorderPenForTank = true;
            if (focusStatus == focus_status_tanking && overrideBorderPenForTank)
            {
                barBorderPen = tankingBorderPen;
            }
            const bool overrideBrushForTakingDamage = true;
            if (focusStatus == focus_status_tanking || focusStatus == focus_status_damaged_but_not_tanking
                && overrideBrushForTakingDamage)
            {
                if (player == "You")
                {
                    fillBarBrush = YourBrushForTakingDamage;
                    PlayerFontBrush = Brushes.WhiteSmoke;
                }
                else
                {
                    fillBarBrush = BrushForTakingDamage;
                    PlayerFontBrush = Brushes.WhiteSmoke;
                }
            } 
            
            g.FillRectangle(fillBarBrush, theBar);

            ////THIS WILL DRAW THE DAMAGE, But noticing that healing aggro didnt account for much i will make that part of the rectangle a different color instead of the damage component being just red.

            ////now is a good place to put the aggro from damage...
            //try
            //{
            //    Dictionary<string, Dictionary<string, long>> damagesByPlayers = this.calc.damagesByPlayerOnTargets;
            //    Dictionary<string, long> damagesByPlayerOnTargets = damagesByPlayers[player];
            //    long damageOnTarget = damagesByPlayerOnTargets[enemy];
            //    float normalizedDamage = (float)(damageOnTarget / (float)maxAmountByPlayer);
            //    int damBarWidth = (int)(normalizedDamage * client.Width);
            //    int damBarTop = barTop;
            //    Rectangle damBar = new Rectangle(client.Left, client.Top + damBarTop, damBarWidth, barHeight);
            //    g.FillRectangle(Brushes.Red, damBar);
            //}
            //catch
            //{

            //}
            ////end: draw aggro from damage

            //draw bar outline
            //now is a good place to put the aggro from healing...
            try
            {
                long damageOnTarget = 0;

                try
                {
                    Dictionary<string, Dictionary<string, long>> damagesByPlayers = this.calc.damagesByPlayerOnTargets;
                    Dictionary<string, long> damagesByPlayerOnTargets = damagesByPlayers[player]; //this is indexed by player first then enemy
                    damageOnTarget = damagesByPlayerOnTargets[enemy];
                }
                catch //player may not have done any damage so default to zero, although he may have heal aggro so some threat too.
                {
                }
                float threatRating = 1f;
                if (playersList.ContainsKey(player))
                {
                    threatRating = playersList[player].ThreatRating;
                }
                long damageAggro = (long)(damageOnTarget * threatRating);
                float normalizedDamage = (float)(damageOnTarget / (float)maxAmountByPlayer);
                int damBarWidth = (int)(normalizedDamage * client.Width);

                //THIS HASHTABLE IS SORTED BY ENEMY THEN BY PLAYER.
                //healing aggro is total aggro minus damage aggro so notation-wise hash[enemy][player]

                Hashtable enemyAggroTable = (Hashtable)calc.htGroupThreatFromLog[enemy];
                float totalAggroOnTarget = (float)enemyAggroTable[player];
                long healAggroOnTarget = (long)totalAggroOnTarget - damageAggro;
                float normalizedHealAggro = (float)(healAggroOnTarget / (float)maxAmountByPlayer);
                int healBarWidth = (int)(normalizedHealAggro * client.Width);
                if (healBarWidth > 0)
                {
                    int damBarTop = barTop;
                    Rectangle healBar = new Rectangle(client.Left + damBarWidth, client.Top + damBarTop, healBarWidth, barHeight);
                    g.FillRectangle(Brushes.Red, healBar);
                }
                //end: draw aggro from damage
            }
            catch (Exception ex)
            {

            }
            //draw bar outline
            if (barBorderPen != null)
                g.DrawRectangle(barBorderPen, theBar);
            
            bool drawFontOnBar = barHeight > 4; //6 pixels is about max the eye can see
            if (drawFontOnBar)
            {
                const int maxFontHeightPixels = 12;
                int fontHeighPixels = barHeight > maxFontHeightPixels ? maxFontHeightPixels : barHeight;
                Font drawPlayerFont = new Font(FontFamily.GenericSerif, fontHeighPixels, System.Drawing.GraphicsUnit.Pixel);

                if (drawPlayerFont != null)
                    g.DrawString(player + symbol, drawPlayerFont, PlayerFontBrush, new PointF(client.Left, client.Top + barTop));
            }
            dBars.Add(new EnemyPlayerPair(enemy, player), theBar);

            rank++;
        }

        private void pbDamage_Click(object sender, EventArgs e)
        {
            DrawDamages(pbDamage.CreateGraphics(), true);
        }

        private void pbHealing_Click(object sender, EventArgs e)
        {
            DrawHeals(pbHealing.CreateGraphics(), true, true);
        }

        private void pbHealing_Paint(object sender, PaintEventArgs e)
        {
            DrawHeals(e.Graphics, true, true);
        }

        private void pbHealing_Resize(object sender, EventArgs e)
        {
            DrawHeals(pbHealing.CreateGraphics(), true, true);
        }

        private void tpDamageGraph_Resize(object sender, EventArgs e)
        {
            DrawDamages(pbDamage.CreateGraphics(), true);
        }

        private void tsmiWindowOnTop_Click(object sender, EventArgs e)
        {
            this.TopMost = tsmiWindowOnTop.Checked;
            Properties.Settings.Default.WindowOnTop = this.TopMost;
            Properties.Settings.Default.Save();
        }


    /// <summary>
    /// //////////////////////////
    /// //////////////////////
    /// START OF BAR DRAWING CODE
    /// //////////////////////
    /// /////////////////////////
    /// </summary>
    /// <param name="g"></param>
    ///


    private void DrawThreat(Graphics g, bool forceDraw)
    {
        int clientWidth = pbThreat.ClientRectangle.Width;
        int clientHeight = pbThreat.ClientRectangle.Height;

        dBars.Clear();

        Dictionary<string, List<ReportingEvent>> reports = GetThreatReport();
        
        //List<ReportingEvent> playersByAmount = calc.GetPlayersSortedByThreatOnTarget(playerName, FocusTarget);
        long grandThreatTotal = 0;
        if ((reports == null || reports.Count == 0))
        {
            grandThreatTotal = 0;
        }
        else
        {
            grandThreatTotal = 0;
            foreach (string key in reports.Keys)
            {
                List<ReportingEvent> threatTable = (List<ReportingEvent>)reports[key];//calc.htGroupThreatFromLog[key];
                long threatCount = 0;
                foreach (ReportingEvent ev_Player in threatTable)
                {
                    long threat = Convert.ToInt64(((ReportingEvent)ev_Player).Amount);
                    threatCount += threat;
                }
                grandThreatTotal += threatCount;
            }
        }
                

        if (grandThreatTotal !=lastThreatAmount || forceDraw)
        {            
            ///dont forget this line below is because the value was totally consumed and we are updating its value.
            lastThreatAmount = grandThreatTotal;

            g.FillRectangle(fillBgBrush, 0, 0, clientWidth, clientHeight);


            htOfClientRect.Clear();

            //Debug.Assert(true);
            if (reports == null || reports.Count == 0)
            {
                g.Flush(); 
                return;
            }

            if (FocusTarget != null && FocusTarget != "")
            {
#region "Focus target not empty, Single Target view"

                Rectangle clientRect = new Rectangle(0, 0, clientWidth, clientHeight);
                        
                //if (maxedPlayer == null)
                //{
                    
                //} 
                //bool focusChanged = lastFocusTarget != FocusTarget;
    //&& FocusTarget != null && lastFocusTarget != null;

                string playerTakingDamage = null;
                if (this.dDamageEventsOnTargets != null && dDamageEventsOnTargets.ContainsKey(FocusTarget))
                {
                    List<ParsedLogEvent> eventList = dDamageEventsOnTargets[FocusTarget];
                    if (eventList != null && eventList.Count > 0)
                        playerTakingDamage = ((ParsedLogEvent)(eventList[eventList.Count - 1])).Target;
                    else
                        playerTakingDamage = null;
                }
                ///FIND PLAYER WITH MAX THREAT, BASE THE GRAPH ON HIM, NORMALIZED
                //if (focusChanged)
                //{
                //    if (IsLogging && focusChanged && playerTakingDamage != null 
                //        && playerTakingDamage != lastPlayerTakingDamage && !IsPossibleEnemy(maxedPlayer))
                //    {
                //        //InfoLogIt("Threat Warning. " + maxedPlayer + " may be drawing aggro soon " + FocusTarget, null, maxedPlayer + " might have aggro on " + FocusTarget);
                //        InfoLogIt("Threat Warning. " + maxedPlayer + " may be drawing aggro soon " + FocusTarget, null, "Threat warning.");
                //    }
                //    lastFocusTarget = FocusTarget;
                //    lastPlayerTakingDamage = playerTakingDamage;
                //}

                lastPlayerWithThreatOnTarget = maxedPlayer;
                Hashtable hateTable = (Hashtable)calc.htGroupThreatFromLog[FocusTarget];            
                List<ReportingEvent> reportsListOfHate = calc.GetReportingEventsFromThreatTable(hateTable);
                maxedPlayer = GetPlayerWithMaxThreatOn(FocusTarget, reportsListOfHate);

                long maxAmountByPlayer = -1;

                if (maxedPlayer != null)
                {
                    maxAmountByPlayer = Convert.ToInt64(hateTable[maxedPlayer]); 
                    DrawThreatStats(g, reportsListOfHate, clientRect,
                        GraphTypes.Threat, lastThreatAmount, maxAmountByPlayer, FocusTarget);
                }
                else
                {

                }

     #endregion
            }
            else
            {
#region "Multi-target view"
                //    if (playersByAmount != null)
                //    {
                //    }
                //    else
                //    {
                //#region "DrawMultipleTargetThreatDisplays"

                ///MAKE LIST OF ITEMS TO TARGET
                //string enemyTarget = mParser.CurrentTarget; //show current threat target
                //lCurrentThreatTarget.Text = enemyTarget;

                //object[] itemsToAddToComboBox_Targets = GetTargetNamesForComboBoxItems();
                //cbHateFocusTarget.Items.Clear();
                //cbHateFocusTarget.Items.AddRange(itemsToAddToComboBox_Targets);
                //cbHateFocusTarget.Text = ""; //clear the focus display to user


                //add list of all possible reports on target to combobox for selection
                foreach (string reportTarget in reports.Keys)
                {
                    if (!cbHateFocusTarget.Items.Contains(reportTarget))
                        cbHateFocusTarget.Items.Add(reportTarget);
                }

                if (!cbHateFocusTarget.Items.Contains(FocusAllString))
                    cbHateFocusTarget.Items.Add(FocusAllString);

                int totalCount = reports.Count;
                const int maxDrawn = 30;
                int drawCount = totalCount > maxDrawn ? maxDrawn : totalCount; ///max function can apply

                if (drawCount != 0 && clientHeight != 0 && clientWidth != 0)
                {
                    double clientWtoHRatio = (double)clientWidth / clientHeight;
                    //long area =  clientWidth * clientHeight ;
                    //int sqrtArea = Math.Sqrt(area);
                    //long areaPerCountDrawn = area / drawCount;


                    ////int maxSize = clientWidth > clientHeight ? clientWidth : clientHeight;
                    ////int countX = (int)Math.Sqrt((drawn * clientWtoHRatio));  //(int)(Math.Sqrt((double)drawn));
                    ////int countY = (int)(maxDrawn / countX);
                    //////algebra countX * countY = drawn; 
                    ////int width = (int)(clientWidth / countX);
                    ////int height = (int)(clientHeight / countY);
                    ////int remainder = drawn - countX * countY;

                    //width * height = AreaOfClient
                    //width/height = clientWtoHRatio
                    //so height = width/clientWtoHRatio
                    //width =areaPerCountDrawn/(width/clientratio) = \

                    int projectedXDrawCount;
                    int width;
                    int projectedYDrawCount;
                    int height;
                    int countX;
                    int countY;

                    if (drawCount == 1)
                    {
                        countX = 1;
                        countY = 1;
                        width = clientWidth;
                        height = clientHeight;
                    }
                    else
                    {
                        projectedXDrawCount = (int)(Math.Sqrt(drawCount) * (double)clientWidth / clientHeight);
                        if (projectedXDrawCount != 0)
                            projectedXDrawCount = projectedXDrawCount > drawCount ? drawCount : projectedXDrawCount;
                        else
                            projectedXDrawCount = drawCount;

                        projectedYDrawCount = (int)((double)drawCount / projectedXDrawCount + 1); //ROUND UP

                        if (projectedXDrawCount != 0)
                            projectedYDrawCount = drawCount % projectedXDrawCount == 0 ? projectedYDrawCount - 1 : projectedYDrawCount;
                        else
                            projectedXDrawCount = drawCount;

                        countX = projectedXDrawCount;//(int)(clientWidth / (float)width);
                        countY = projectedYDrawCount;//(int)(clientHeight / (float)height);
                        height = (int)((double)clientHeight / (countY));//(int)((float)clientWtoHRatio * areaPerCountDrawn / clientHeight);
                        width = (int)((double)clientWidth / countX);//(int)((float)clientWtoHRatio * areaPerCountDrawn / clientWidth);
                    }

                    htOfClientRect.Clear(); //clear list for prep for reloading
                    int idx = 0;

                    Point endPoint = new Point(clientWidth, clientHeight);
                    ThreatBarBrush = new LinearGradientBrush(new Point(0, 0), endPoint, Color.LightYellow, Color.OrangeRed);
                    YourBrushForBarFilling = new LinearGradientBrush(new Point(0, 0), endPoint, Color.Orange, Color.OrangeRed);
                    HatedTargetBrushForBarFilling = new LinearGradientBrush(new Point(0, 0), endPoint, Color.Brown, Color.OrangeRed);

                    foreach (string target in reports.Keys)
                    {
                        List<ReportingEvent> playersByAmount = (List<ReportingEvent>)reports[target];


                        //calculate row, column
                        int row = (int)((float)idx / countX);
                        int col = countY == 0 ? idx : idx % countX;

                        int cLeft = col * width;
                        int cTop = row * height;
                        //compute a rectangle for position and size info
                        Rectangle clientRect = new Rectangle(cLeft, cTop, width, height);
                        htOfClientRect.Add(target, clientRect); //save thier sizes for comparison to click point

                        //draw threat bars
                        long totalAmount = calc.GetTotalThreat(playersByAmount);
                        string maxedPlayer;//will be filled below
                        long maxAmountByPlayer = calc.GetMaxThreatFromPlayer(out maxedPlayer, playersByAmount);

                        DrawThreatStats(g, playersByAmount, clientRect, GraphTypes.Threat, totalAmount, maxAmountByPlayer, target);
                        //g.Flush();
                        idx++;
                    }

                }
                //else
                //{
                //    g.FillRectangle(fillBgBrush, 0, 0, pbDamage.ClientSize.Width, pbDamage.ClientSize.Height);
                //}
#endregion
            }
        }

        g.Flush();  
    }

    private string GetPlayerWithMaxThreatOn(string FocusTarget, List<ReportingEvent> reportsListOfHate)
    {
        string maxedPlayer = null;//will be filled below
        long maxTotalByPlayer = 0;

        if (reportsListOfHate == null || reportsListOfHate.Count == 0)
        {
            return null;
        }
        else
        {
            foreach (ReportingEvent hateReport in reportsListOfHate)
            {
                string plyrName = hateReport.TargetName;
                if (plyrName == null || plyrName == "")// && !player.Contains("'"))
                {
                    Debug.Assert(true);
                }
                else
                {
                    long amount = hateReport.Amount;
                    if (amount >= maxTotalByPlayer)
                    {
                        maxTotalByPlayer = amount;
                        maxedPlayer = plyrName;
                    }
                }
            }

            return maxedPlayer;
        }
    }

        

//private string GetPlayerWithMaxThreatOn(string tar)
//{
//    string maxedPlayer = null;//will be filled below
//    long maxTotalByPlayer = 0;

//    if (calc.htGroupThreatFromLog == null || calc.htGroupThreatFromLog.Count == 0)
//    {
//        return null;
//    }
//    else
//    {
//        foreach (string plyrName in calc.htGroupThreatFromLog.Keys)
//        {
//            if (playerName == null || playerName == "")// && !player.Contains("'"))
//            {
//                Debug.Assert(true);
//            }
//            else
//            {
//                Hashtable hateTable = (Hashtable)calc.htGroupThreatFromLog[playerName];
//                long amount = hateTable == null ? 0 : Convert.ToInt64(hateTable[tar]);
//                if (amount >= maxTotalByPlayer)
//                {
//                    maxTotalByPlayer = amount;
//                    maxedPlayer = plyrName;
//                }
//            }
//        }

//        return maxedPlayer;
//    }

//}


        private static long GetEventAmountCount(List<ReportingEvent> playersByAmount)
        {
            long amountCount = 0;
            if (playersByAmount == null) amountCount = 0;
            else
                for (int i = 1; i <= playersByAmount.Count; i++)
                {
                    amountCount += playersByAmount[i].Amount;
                }
            return amountCount;
        }




        private Dictionary<string, List<ReportingEvent>> GetThreatReport()
        {
            if (calc.htGroupThreatFromLog == null || calc.htGroupThreatFromLog.Count == 0)
                return null;

            Dictionary<string, List<ReportingEvent>> reports = new Dictionary<string, List<ReportingEvent>>();//of List<ReportingEvent>

            foreach (string target in calc.htGroupThreatFromLog.Keys)
            {
                //bool targetContSp = target.Contains(" ");
                //bool isKnownEnemy = enemiesList.ContainsKey(target) || target.Contains(" ");
                //bool isKnownPlayer = playersList.ContainsKey(target) && !targetContSp;
                //if (isKnownEnemy || (!isKnownPlayer & target.ToLower() !="you") )
                if (IsPossibleEnemy(target))
                {

                    List<ReportingEvent> playersByAmount = calc.GetPlayersSortedByThreatOnTarget(target);

                    const int minPlayersToShow = 1;
                    if (playersByAmount != null && playersByAmount.Count >= minPlayersToShow)
                    {
                        reports.Add(target, playersByAmount);
                    }
                }

            }
            return reports;
        }

        private object[] GetTargetNamesForComboBoxItems()
        {
            List<string> items = calc.htGroupThreatFromLog.Keys.OfType<string>().ToList();
            items.Sort(); //turn it into  a sortable list
            object[] itemsToAddToComboBox_Targets = items.ToArray<object>();
            return itemsToAddToComboBox_Targets;
        }

        //private string GetProbableRaidTarget()
        //{
        //    return this.mParser.CurrentTarget;
        //    //string probableTarget = null;
        //    //const int minTargetsCanHaveBeforeFound = 2;
            
        //    //foreach (string enemyTarget in calc.htGroupThreatFromLog)
        //    //{
        //    //    if (FindInKnownEnemiesList(enemyTarget) || !FindInKnownPlayersList(enemyTarget))
        //    //    {
        //    //        //get players who have aggro
        //    //        //get the aggro table
        //    //        Hashtable aggroTable=(Hashtable)calc.htGroupThreatFromLog[enemyTarget];
        //    //        int count = aggroTable.Count;
        //    //        if (count < minTargetsCanHaveBeforeFound)
        //    //            continue;
                    
        //    //        //if players have threat on boss and boss has threat on each player
        //    //        if (!calc.htGroupThreatFromLog[
        //    //        //get total threat
        //    //        return enemyTarget; //return first target
        //    //    }
        //    //}
        //    //return probableTarget;
        //}
        private Dictionary<string, Brush> dPlayerBrushes = new Dictionary<string, Brush>();
        private Dictionary<string, Pen> dPlayerPens = new Dictionary<string, Pen>();

        const string symbol_tanking = " <->";
        const string symbol_damaging_but_not_tanking = " ->";
        const string symbol_hate_focus_but_not_tanking = " <-";
        const int focus_status_tanking = 1;
        const int focus_status_damaging_but_not_tanking = 2;
        const int focus_status_damaged_but_not_tanking = 3;

        private void DrawThreatStats(System.Drawing.Graphics g, 
            List<ReportingEvent> playersByAmount, Rectangle clientRect, 
            GraphTypes barGraphType, long totalAmount, long maxAmountByPlayer, string targetName)
        {
            if (playersByAmount == null) return;

            const int maxFontHeightPixels = 12;

            ////e.ClipRectangle
            //int maxBarWidth = pbThreat.ClientSize.Width;

            string yourFocusTarget = TargetOf("You");
            if (yourFocusTarget != null && yourFocusTarget != "")
            {
                g.DrawRectangle(new Pen(Color.Yellow, 2f), clientRect); //draw "selection rectangle" to indicate your current target...

            }
            else
            {
                g.DrawRectangle(Pens.Black, clientRect);

            }

            //g.FillRectangle(fillBgBrush, clientRect.Left, clientRect.Top, clientRect.Width, clientRect.Height);
            if (clientRect.Width > 2)
                clientRect.Inflate(new Size(-2, 0)); //add room for border
            if (clientRect.Height > 2)
                clientRect.Inflate(new Size(0, -2));

            int playerCount = playersByAmount.Count;
            if (playersByAmount != null && playerCount != 0)
            {
                int barHeight = (int)(clientRect.Height  / ((float)playerCount + 1f));
                if (barHeight == 0)
                    return;

                //int fontHeighPixels = 2 * barHeight > maxFontHeightPixels ? maxFontHeightPixels : 2 * barHeight;

                Font drawPlayerFont = null;

                int captionHeight = barHeight > maxFontHeightPixels ? maxFontHeightPixels : barHeight;

                drawPlayerFont = new Font(FontFamily.GenericSerif, captionHeight, 
                    System.Drawing.GraphicsUnit.Pixel);


                g.FillRectangle(Brushes.Blue, clientRect.Left, clientRect.Top, clientRect.Width, captionHeight);
                const bool drawTargetName = true;

                if (drawTargetName)
                {
                    //draw target name...
                    //Font targetNameFont = new Font(FontFamily.GenericSerif, fontHeighPixels == 0 ? 1 : fontHeighPixels,
                    //    System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Pixel);
                    PointF drawTargetPoint = new PointF(clientRect.Left, clientRect.Top);
                    g.DrawString(targetName, drawPlayerFont, Brushes.LightGray, drawTargetPoint);
                }
                //////////////////////////////////////////////////////////////////////
                //DamageFillBarBrush = new LinearGradientBrush(new Point(0,0), new Point(1, 1), Color.
                //HealingFillBarBrush = Brushes.GreenYellow;

                //long totalAmount = calc.GetTotalThreat();

                //string maxedPlayer = null;
                //long maxAmountByPlayer = calc.GetMaxThreatByPlayer(out maxedPlayer);
                ///////////////////////////////////////////////////////////////////////

                //compute new bar height including the current preferred bar height as a factor
                barHeight = (int)((clientRect.Height - captionHeight) / ((float)playerCount));

                barHeight = (barHeight > MAX_BarHeight) ? MAX_BarHeight : barHeight; //MAX fuction essentially of itself for convenience and the enforced maximum.

                if (!(totalAmount == 0 || playerCount == 0))
                {
                    int rank = 0; //start at bar below top one (which is reserved for the target name)

                    foreach (ReportingEvent playerEvent in playersByAmount)
                    {
                        int barTop = (int)(rank * barHeight) + captionHeight;
                        long amount = playerEvent.Amount;

                        List<ParsedLogEvent> lstDamages = null;
                        if (dDamageEventsOnTargets != null && dDamageEventsOnTargets.ContainsKey(targetName))
                        {
                            try
                            {
                                if (dDamageEventsOnTargets.ContainsKey(playerEvent.TargetName))
                                    lstDamages = dDamageEventsOnTargets[playerEvent.TargetName];
                            }
                            catch { }
                        }

                        string focusesTarget = TargetOf(targetName);

                        int focusStatus = 0;
                        string sSymbol = "";

                        if (focusesTarget != null && focusesTarget == playerEvent.TargetName)
                        {
                            string focusesTargets_Target = TargetOf(focusesTarget);
                            if (playersList.ContainsKey(focusesTarget))
                            {
                                if (focusesTargets_Target == targetName)
                                {
                                    //Color cFocusTarget = pen.Color;
                                    //Brush targetBrush = new HatchBrush(HatchStyle.SolidDiamond, cFocusTarget);
                                    //br = targetBrush;
                                    
                                    sSymbol = symbol_tanking;

                                    focusStatus = focus_status_tanking;
                                }
                                else
                                {
                                    //Color cTar = pen.Color; //get color for player
                                    //Brush bTarget = new HatchBrush(HatchStyle.BackwardDiagonal, cTar); //create a new brush for the player to indicate threat.
                                    //br = bTarget; //set the brush

                                    sSymbol = symbol_hate_focus_but_not_tanking;
                                    focusStatus = focus_status_damaged_but_not_tanking;
                                }
                            }
                        }
                        else
                        {
                            if (TargetOf(playerEvent.TargetName) == targetName)
                            {
                                focusStatus = focus_status_damaging_but_not_tanking;
                                sSymbol = symbol_damaging_but_not_tanking;
                            }
                        }

                        bool include = false;
                        if (focusStatus == focus_status_tanking && tsmiPlayersTankingFilter.Checked)
                        {
                            include = true;
                        }
                        if (focusStatus == focus_status_damaging_but_not_tanking && tsmiPlayersWithAggroFilter.Checked)
                        {
                            include = true;
                        }
                        else if (focusStatus == focus_status_damaged_but_not_tanking && tsmiPlayersWithAggroFilter.Checked)
                        {
                            include = true;
                        }
                        else
                        {
                            if (tsmiOtherPlayersFilter.Checked)
                            {
                                include = true;
                            }
                            else
                                include = false;
                        }
                        if (include == false)
                        {
                            //rank++;
                            continue; //GO TO BOTTOM OF LOOP skipping past drawing this element
                        }
                        else
                        {
                            Brush br = GetPlayerBrush((string)playerEvent.TargetName, barGraphType);
                            Pen pen = GetPlayerPen((string)playerEvent.TargetName, barGraphType);


                            string playerText = playerEvent.TargetName;

                            DrawNormalizedBar(g, br, pen, amount, clientRect, captionHeight, barHeight, maxAmountByPlayer, rank, playerText, sSymbol, focusStatus, targetName);

                            rank++;
                        }
                    }

                    DrawHorizontalAxis(g, clientRect, maxAmountByPlayer, true);

                }
            }
            //g.Flush();
        }
        public enum GraphTypes
        {
            Threat,Healing,Damage
        }

        public Brush GetPlayerBrush(string playerName, GraphTypes graphType)
        {
            Player player = FindPlayerByName(playerName);
            if (playerName == "You")
            {
                Debug.Assert(true==true==true);
            }

            if (player == null)
            {
                switch (graphType)
                {
                    case GraphTypes.Damage:
                        return this.DamageFillBarBrush;
                    case GraphTypes.Healing:
                        return this.HealingFillBarBrush;
                    case GraphTypes.Threat:
                        return this.ThreatBarBrush;
                    default:
                        return this.ThreatBarBrush;
                }
            }
            else
            {
                if (player.graphColor == Color.Empty)
                {
                    switch (graphType)
                    {
                        case GraphTypes.Damage:
                            return this.DamageFillBarBrush;
                        case GraphTypes.Healing:
                            return this.HealingFillBarBrush;
                        case GraphTypes.Threat:
                            return this.ThreatBarBrush;
                        default:
                            return this.ThreatBarBrush;
                    }  
                }

                if (player.BrushForCharts == null)
                {
                    player.CreateSolidBrush();
                }
                if (player.BrushForCharts !=null)
                    return player.BrushForCharts;
                else
                    switch (graphType)
                    {
                        case GraphTypes.Damage:
                            return this.DamageFillBarBrush;
                        case GraphTypes.Healing:
                            return this.HealingFillBarBrush;
                        case GraphTypes.Threat:
                            return this.ThreatBarBrush;
                        default:
                            return this.ThreatBarBrush;
                    } 
            }
        }
        //Pen LastPlayerTakingDamage = new Pen(Color.Red);
        //Pen PlayerYouPen_TakingDamage = new Pen(Color.DarkRed);
        //Pen PlayerYouPen_NotTakingDamage = new Pen(Color.Yellow);

        public Pen GetPlayerPen(string p, GraphTypes graphType )
        {
            if (p == "You")
            {
                Debug.Assert(true);
            }

            Player player = FindPlayerByName(p);
            if (player == null)
            {
                switch (graphType)
                {
                    case GraphTypes.Damage:
                        return DamageBarPen;
                    case GraphTypes.Healing:
                        return HealingBarPen;
                    case GraphTypes.Threat:
                        return ThreatBarPen;
                    default:
                        return ThreatBarPen;
                }
            } else
            {
                Pen playerPen = player.PenForCharts;
                if (player.graphColor == Color.Empty)
                {
                    return ThreatBarPen;
                }

                if (playerPen == null)
                    player.CreatePen();
                if (player.PenForCharts != null )
                    return playerPen;
                else
                    return ThreatBarPen;
                //if (p == lastPlayerTakingDamage)
                //{
                //    if (p == "You") 
                //        return PlayerYouPen_TakingDamage;
                //    else 
                //        return LastPlayerTakingDamage;
                //}
                //else
                //{
                //    if (p == "You") 
                //        return PlayerYouPen_NotTakingDamage;
                //    else
                //    {
                //    Pen playerPen = player.PenForCharts;
                //    if (playerPen == null)
                //        player.CreatePen();
                //    return playerPen;
                //    }
                //}
            }
        }
        public Player FindPlayerByName(string name)
        {
            if (playersList == null || playersList.Count == 0)
                return null;

            foreach (Player player in playersList.Values)
            {
                if (player.Name == name)
                {
                    return player;
                }
            }
            return null;
        }

        private void pbThreat_Click(object sender, EventArgs e)
        {
            DrawThreat(true);
        }

        private void btnClearFocusTarget_Click(object sender, EventArgs e)
        {
            cbHateFocusTarget.Text = FocusAllString;
            DrawThreat(true);
        }

        Point lastPoint = Point.Empty;
        string oldCaptionFor_Player = null;//tsmiAddTargetAsKnownPlayerAndRemoveThreatTable.Text;

        private void pbThreat_MouseDown(object sender, MouseEventArgs e)
        {
                if (e.Button == MouseButtons.Left)
                {
                    if (FocusTarget == null || FocusTarget == FocusAllString)
                    {
                        FocusTarget = GetAreaClicked(e.Location);

                        ///SHOULD OPEN UP A WINDOW WITH THAT CONTROL IN IT LOOKING AT THREAT ON THE TARGET
                        DrawThreat(true);
                    }
                    else
                    {
                        FocusTarget = null;
                    } 
                }
                else
                {
                    //string oldCaptionFor_Enemy = tsmiAddTargetAsKnownEnemyAndRemoveFromThreatTable.Text;

                    lastPoint = e.Location;
                    //EnemyPlayerPair pair = GetBarInfoFromClickedPoint(e.Location);
                    
                    //if (pair != null)
                    //{
                    //    //tsmiAddTargetAsKnownPlayerAndRemoveThreatTable.Visible = true;
                    //    //tsmiAddTargetAsKnownEnemyAndRemoveFromThreatTable.Text = oldCaptionFor_Enemy.Replace("{Target}", pair.Player);
                    //}
                    //else
                    //{
                    //    //tsmiAddTargetAsKnownPlayerAndRemoveThreatTable.Visible = false;
                    //}
                    if (FocusTarget == null || FocusTarget == FocusAllString)
                    {
                        FocusTarget = GetAreaClicked(e.Location);
                    }

                    tsmiAddTargetAsKnownPlayerAndRemoveThreatTable.Text = oldCaptionFor_Player.Replace("{Target}", FocusTarget);

                    cmsThreatTableCommands.Show(pbThreat, e.Location);
                    //tsmiAddTargetAsKnownEnemyAndRemoveFromThreatTable.Text = oldCaptionFor_Enemy;
                    //

                }

                //cbHateFocusTarget.Text = FocusTarget;
        }
        private string GetAreaClicked(System.Drawing.Point point)
        {
            //due search of what rectangle the user clicked
            foreach (string clientRectanglesTarget in htOfClientRect.Keys)
            {
                Rectangle r = (Rectangle)htOfClientRect[clientRectanglesTarget];
                if (r.Contains(point))
                    return clientRectanglesTarget;
            }
            return FocusAllString;
        }

        private void cbHateFocusTarget_SelectedIndexChanged(object sender, EventArgs e)
        {
            DrawThreat(true);
        }

        private void DrawThreat(bool forceDraw)
        {
            if (pbThreat.IsDisposed)
            {
                return;
            }
            DrawThreat(pbThreat.CreateGraphics(), forceDraw);
        }

        private void tsmiMoveCheckedFromDandHToKnownPlayers_Click(object sender, EventArgs e)
        {
            //damage nodes
            foreach (TreeNode damNode1 in damageRoot.Nodes)
            {
                if (damNode1.Checked && damNode1 != damagesTotalNode)
                {
                    string target = GetNameByNode(damNode1);
                    bool added = AddKnownPlayer(target, false) != null;
                }
                damNode1.Checked = false;
            }

            //healing nodes
            foreach (TreeNode healNode1 in healingRoot.Nodes)
            {
                if (healNode1.Checked && healNode1 != healsTotalNode)
                {
                    string target = GetNameByNode(healNode1);
                    bool added= AddKnownPlayer(target, false) !=null;
                        //nodesToRemove.Add(healNode1);
                }
                healNode1.Checked = false;
            }
            ////remove both damage and healing nodes if they were added to enemy list
            //foreach (TreeNode nToDelete in nodesToRemove)
            //{
            //    if (nToDelete != null && nToDelete.Checked)
            //    {
            //        tvDamageAndHealing.Nodes.Remove(nToDelete);
            //    }
            //}
            PlayersListEditorControl.RefreshItems();
        }

        private void btnClearAllThreat_Click(object sender, EventArgs e)
        {
            //btnClearAllThreatNow.Visible = false;
            ClearRaidThreat();
        }

        //int hintTimes = 0;
        //bool startTreasureHints = false;
        //int maxTreasureHintTimes = 3;
        //private string lastFocusTarget;

        //private void tmrDamageCombatIndicator_Tick(object sender, EventArgs e)
        //{
        //    HandleCoppice();
        //}

        //private void HandleCoppice()
        //{
        //    const int periodicityOfDamage = 2;
        //    const int periodicityOfHints = 5;
        //    const string msgForBossDown = "Boss down!";
        //    const string msgToKillTreasure = "Kill the Trial Treasures immediately!";

        //    if (calc.Coppice_GroupDamagingStarted == true && IsLogging)
        //    {
        //        TimeSpan dt = DateTime.Now - calc.LastDamageEvent.TimeStamp;

        //        if (dt.TotalSeconds > periodicityOfDamage)
        //        {
        //            calc.Coppice_GroupDamagingStarted = false;
        //            calc.Coppice_BossDown = true;
        //            InfoLogIt(msgForBossDown, null, msgForBossDown);
        //            startTreasureHints = true;
        //        }
        //    }

        //    if (calc.Coppice_InCoppice == true && calc.Coppice_BossDown && startTreasureHints)
        //    {
        //        hintTimes++;
        //        TimeSpan dt = DateTime.Now - calc.LastDamageEvent.TimeStamp;
        //        if (hintTimes > maxTreasureHintTimes)
        //        {
        //            startTreasureHints = false;
        //        }
        //        if (hintTimes % periodicityOfHints == 0)
        //        {
        //            InfoLogIt(msgForBossDown, null, msgForBossDown);

        //            if (calc.Coppice_TreasureDamaged == false)
        //                InfoLogIt(msgToKillTreasure, null, msgToKillTreasure);
        //        }
        //    }
        //}

        ////
        ////

        //public bool IsLogging
        //{
        //    get { return cbLogging.Checked; }
        //}

        private void tsmiAddTargetAsKnownPlayerAndRemoveThreatTable_Click(object sender, EventArgs e)
        {
            AddKnownPlayer(FocusTarget, true); //enemy turned out to be a player
            if (calc.htGroupThreatFromLog.ContainsKey(FocusTarget))
                calc.htGroupThreatFromLog.Remove(FocusTarget);
            DrawThreat(true);
        }

        private EnemyPlayerPair GetBarInfoFromClickedPoint(Point clickedPoint)
        {
            EnemyPlayerPair clicked = null;
            foreach (EnemyPlayerPair p in dBars.Keys)
            {
                Rectangle theBar = dBars[p];
                if (theBar.Contains(clickedPoint))
                {
                    clicked = p;
                }
            }
            return clicked;
        }

        
        private void cmsThreatTables_Closed(object sender, ToolStripDropDownClosedEventArgs e)
        {
            tsmiAddTargetAsKnownPlayerAndRemoveThreatTable.Text = oldCaptionFor_Player;
        }


        private void chkBoxTracking_CheckedChanged(object sender, EventArgs e)
        {
            EnableTracking(cbTracking.Checked);
   
        }

        private void EnableTracking(bool enabled)
        {
            if (enabled)
            {
                calc.Damage += new EventHandlers.HandleDamageDelegate(calc_Damage);
                calc.Heal += new EventHandlers.HandleHealDelegate(calc_Heal);
                calc.Cast += new EventHandlers.HandleCastDelegate(calc_Cast);
                calc.Death += new EventHandlers.HandleDeathDelegate(calc_Death);
            }
            else
            {
                calc.Damage -= new EventHandlers.HandleDamageDelegate(calc_Damage);
                calc.Heal -= new EventHandlers.HandleHealDelegate(calc_Heal);
                calc.Cast -= new EventHandlers.HandleCastDelegate(calc_Cast);
                calc.Death -= new EventHandlers.HandleDeathDelegate(calc_Death);
            }
        }

        void calc_Damage(ParsedLogEvent logDamageEvent)
        {
            if (GeneralEvent(logDamageEvent.Actor))
                AppendDamageToTracker(logDamageEvent);
            DoHandleDamageDone(logDamageEvent);
        }

        void calc_Death(DateTime TimeStamp, string actorFullName)
        {
            if (GeneralEvent(actorFullName))
            {
                //AppendTrackerMessage(TimeStamp, actorFullName + " died.");
            }
        }
        /// <summary>
        /// Deprecated as the log has been removed
        /// </summary>
        /// <param name="TimeStamp"></param>
        /// <param name="nonHTMLLine"></param>
       // private void AppendTrackerMessage(DateTime TimeStamp, string nonHTMLLine)
       // {
       //     if (nonHTMLLine == null)
       //         return;//for now
       //     nonHTMLLine = "@" + TimeStamp.ToShortTimeString() + ": " + nonHTMLLine + "\r\n";

       //     try
       //     {
       //         tbTracker.Text = nonHTMLLine + tbTracker.Text;;
       //     }
       //     catch (Exception)
       //     {   
       //     }
       //}



        public static ClassTypes GetClassFromPetName(string fullPetName)
        {
            fullPetName.Trim();
            int ioQuote = fullPetName.IndexOf("'");           
            string shortPetName = null;

            if (ioQuote != -1)
            {
                shortPetName = fullPetName.Substring(ioQuote, fullPetName.Length - ioQuote);
            }

            switch (shortPetName)
            {
                //Necromancer http://aoc.wikia.com/wiki/Spells/Necromancer
                case "Mutilator":
                    return ClassTypes.Necromancer;
                case "Corruptor":
                    return ClassTypes.Necromancer;
                case "Blighted One":
                    return ClassTypes.Necromancer;
                case "Deathless Acolyte: Magus":
                    return ClassTypes.Necromancer;
                case "Necrotic Bomb":
                    return ClassTypes.Necromancer;
                case "Deathless Acolyte: Arcanist":
                    return ClassTypes.Necromancer;
                case "Harvester":
                    return ClassTypes.Necromancer;
                case "Life-Stealer":
                    return ClassTypes.Necromancer;
                case "Deathless Acolyte: Reaper":
                    return ClassTypes.Necromancer;
                case "Dread Archmage: Archmagus":
                    return ClassTypes.Necromancer;
                case "Dread Archmage: Blood Arcanist":
                    return ClassTypes.Necromancer;
                //Demonologist
                case "Demon Guardian":
                    return ClassTypes.Necromancer;
                case "Demon Familiar":
                    return ClassTypes.Demonologist;
                case "Demon Slave":
                    return ClassTypes.Demonologist;
                case "Demon Avenger":
                    return ClassTypes.Demonologist;
                case "Demon Warlord":
                    return ClassTypes.Demonologist;
                default:
                    return ClassTypes.Unknown;
            }
 
        }
        //void calc_PetHeal(ParsedLogEvent damageEvent, string petsName)
        //{                
        //    //should crossreference pets name with class
        //    ClassTypes knownClass = GetClassFromPetName(petsName);

        //    string playerClass = "";
        //    if (knownClass != ClassTypes.Unknown)
        //    {
        //        playerClass = "[" + Player.ClassTypeString(knownClass) + "]";
        //    }

        //    if (GeneralEvent(damageEvent.Actor))
        //    {
        //        AppendTrackerMessage(damageEvent.TimeStamp, damageEvent.Actor + playerClass + " heals " + damageEvent.Target);
        //    }
        //}
        //void calc_PetDamage(ParsedLogEvent damageEvent, string petsName)
        //{
        //    ClassTypes knownClass = GetClassFromPetName(petsName);

        //    string playerClass = "";
        //    if (knownClass != ClassTypes.Unknown)
        //    {
        //        playerClass = "[" + Player.ClassTypeString(knownClass) + "]";
        //    }
            
        //    if (GeneralEvent(damageEvent.Actor))
        //    {
        //        AppendTrackerMessage( damageEvent.TimeStamp, petsName + playerClass + " casts " + damageEvent.Spell + " on " + damageEvent.Target);
        //    }
        //    //should crossreference pets name and or spell with class
        //}

        void calc_Cast(DateTime timeStamp, string player, string spellName)
        {
            if (GeneralEvent(player))
            {
                Spell foundSpell = this.spellsSearcher.GetSpellByName(spellName);
                string classHealing = null;

                if (foundSpell != null)
                {
                    classHealing = "[" + foundSpell.ClassTypeString + "] ";
                }
                //AppendTrackerMessage(timeStamp, player + classHealing + " casts " + spellName);
            }

            //should cross reference spell
        }

        void calc_Heal(ParsedLogEvent logHealEvent)
        {
            //add both targets...dont forget about his friend over there.
            bool lEvent = GeneralEvent(logHealEvent.Actor);
            bool rEvent = GeneralEvent(logHealEvent.Target);
            Spell foundSpell = this.spellsSearcher.GetSpellByName(logHealEvent.Spell);
            string classHealing = null;
            
            if (foundSpell != null)
            {
                classHealing = "[" + foundSpell.ClassTypeString + "] ";
            }

            if (lEvent || rEvent)
            {
                //AppendTrackerMessage(logHealEvent.TimeStamp, logHealEvent.Actor +classHealing+ " healed " + logHealEvent.Target);
            }
            //check spell type against table for type of class
        }

        [Serializable]
        public class KOSPlayerInfo
        {
            private string playerName;

            public string PlayerName
            {
                get { return playerName; }
                set { playerName = value; }
            }
            private string note;

            public string Note
            {
                get { return note; }
                set { note = value; }
            }
            private string speakText;

            public string SpeakText
            {
                get { return speakText; }
                set { speakText = value; }
            }
            public KOSPlayerInfo(string PlayerName, string Note, string SpeakText)
            {
                this.playerName = PlayerName;
                this.note = Note;
                this.speakText = SpeakText;
            }
            public override string ToString()
            {
                return playerName;
            }
        }

        List<string> detectedActors = new List<string>();
        Dictionary<string, KOSPlayerInfo> kosList = new Dictionary<string, KOSPlayerInfo>();
        public Dictionary<string, List<ParsedLogEvent>> dDamageEventsOnTargets = new Dictionary<string, List<ParsedLogEvent>>();

        private void DoHandleDamageDone(ParsedLogEvent logDamageEvent)
        {
            ///
            ///each player will have a has of damages by spell amount and actor or culprit
            ///last spell entered will be saved if it happens by same spell and actor with same time stamp
            ///then it was aoe, else 99% chance it was direct single target damage on player by target
            List<ParsedLogEvent> lastDamages = null;
            //ParsedEvent_Int lastDamageEvent = null;// lastDamages[lastDamages.Count - 1];
            string tar = logDamageEvent.Target;
            string actor = logDamageEvent.Actor;

            if (dDamageEventsOnTargets.ContainsKey(actor))
            {
                lastDamages = dDamageEventsOnTargets[actor];
                //lastDamageEvent = lastDamages[lastDamages.Count - 1];
                //if (lastDamageEvent.Actor == actor)
                //    lastDamages.Clear();
            }
            else
            {
                lastDamages = new List<ParsedLogEvent>();
                dDamageEventsOnTargets.Add(actor, lastDamages);
            }

            if (logDamageEvent != null && logDamageEvent.Actor !=null && logDamageEvent.Target!=null)
            {
                lastDamages.Add(logDamageEvent);

                lastDamages = FilterOutAOE(lastDamages, actor);
                dDamageEventsOnTargets[actor] = lastDamages;
            }
            //if (lastDamageEvent !=null)
            //{
            //    if (lastDamageEvent.Spell == logDamageEvent.Spell &&
            //        lastDamageEvent.TimeStamp == logDamageEvent.TimeStamp)
            //    {
            //        //aoe....AOE AOE
            //        //dont remove until changed, above
            //    }
            //    else
            //    {
            //        lastDamages.Clear();
            //        lastDamages.Add(logDamageEvent);
            //        dDamageDoneByTargets.Add(actor, lastDamages);


            //to save space if on the same target remove it, good enough for targeting purposes
            //if (lastDamageEvent.Target == tar)
            //{
            //    //lastDamages.Remove(lastDamageEvent); //damage on same target
            //    lastDamages.Clear();
            //    if (targetsTargets.ContainsKey(actor))
            //        targetsTargets[actor] = tar;
            //    //else
            //    //    targetsTargets.Add(actor, tar);
            //}

            //else
            //{
            //    if (targetsTargets.ContainsKey(actor))
            //        targetsTargets[actor] = tar;
            //    else
            //        targetsTargets.Add(actor, tar);
            //}
        }

        List<ParsedLogEvent> FilterOutAOE(List<ParsedLogEvent> lastDamages, string actor)
        {
            //SHOULD ADD IF LOGGING

            int damagesCount = lastDamages.Count;
            if (lastDamages.Count > 1)
            {
                //damage by multiple targets exist
                //figure out who has aggro
                //ex: canna, (aoe: belfegor, canna, belfegor) //check timestamp
                //ex: canna, (aoe: belfegor, canna) //check timestamp
                //ex: canna, (direct: belfegor)
                //ex: cannna, (direct: belfegor, belfegor)

                //find repetition indicating that either two or more direct spells were cast on a player (player has aggro)
                //or two or more aoes were cast on the player (player 

                //remove all aoes
                ParsedLogEvent lastEv = null;
                int evIdx = 0;
                string probableTarget = null;
                List<ParsedLogEvent> newDamageTable = new List<ParsedLogEvent>();

                while (evIdx < damagesCount)
                {
                    ParsedLogEvent ev = lastDamages[evIdx];

                    if (lastEv != null) //make sure we're at second on list or greater
                    {
                        if (lastEv.Target != ev.Target)
                        {
                            //same timestamp probable aoe on players
                            while (evIdx < damagesCount)
                            {
                                ev = lastDamages[evIdx];

                                if (lastEv.Spell !=null && lastEv.Spell !="" && 
                                    lastEv.Spell == ev.Spell && lastEv.TimeStamp == ev.TimeStamp)
                                {
                                    evIdx++;
                                }
                                else if (lastEv.Spell == null || lastEv.Spell == "")
                                {
                                    evIdx++;
                                }
                                else
                                {
                                    probableTarget = ev.Target;
                                    newDamageTable.Add(ev);
                                    evIdx++;
                                }
                            }
                            //leave at least one event in there to further filtering by new aoes coming in

                        }
                        else
                        {
                            if (lastEv.Spell == ev.Spell)
                            {
                                //actor name the same we dont need the same spell in there remove it
                            }
                            else if (lastEv.Target != ev.Target)
                            {
                                //same actor doesnt matter if aoe or not, although it probably is
                                //probableTarget = ev.Target;
                                newDamageTable.Add(ev);
                            }
                        }
                    }
                    else
                    {
                        //newDamageTable.Add(ev);
                        //probableTarget = ev.Target;
                    }
                    lastEv = ev;
                    evIdx++;
                }

                return newDamageTable;
        }
            else
                return lastDamages;
        }
        private bool GeneralEvent(string Actor)
        {
            return GeneralEvent(Actor, null);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="Actor"></param>
        /// <returns>Whether or not something should be printed</returns>
        private bool GeneralEvent(string Actor, string Target)
        {
            //bool ignoreYou = !cbShowYouWhenTarget.Checked;//for debugging enable you parsing
            if (Actor == "You" && cbShowYouWhenActor.Checked==false)
            {
                return false;
            }
            else if (Target !=null && Target=="You" && cbShowYouWhenTarget.Checked == false)
            {
                return false;
            }
                
            if (IsLogging && Actor != null && Actor != "")
            {
                int trackModeIndex = cboTrackMode.SelectedIndex;
                //can do alot here
                //gain class information from spell type
                //gain name of player
                //filter by type of filtering scheme, etc.
                if (trackModeIndex == 1)
                {
                    //kos list only
                    if (kosList.ContainsKey(Actor))
                    {
                        ///
                        if (detectedActors.Contains(Actor))
                        {
                            //dont speak but log
                            //dont relog either
                            return false;
                        }
                        else
                        {
                            //speak and log
                            detectedActors.Add(Actor);
                            
                            //ListEditorControl2.AddByItemByText(Actor, true);
                            lbRecentlyDetected.Items.Add(Actor);

                            SpeakEnemyKOS(Actor);
                            return true;
                        }

                    }
                    else
                    {
                        return false;
                    }

                }
                else if (trackModeIndex == -1 || trackModeIndex == 0)
                {
                        //if (Actor != null && Actor != "" && Actor != "You")
                        //everything (less yourself and friendlies)
                        if (!detectedActors.Contains(Actor))
                        {
                            detectedActors.Add(Actor);
                            // AddByItemByText(Actor, true);
                            lbRecentlyDetected.Items.Add(Actor);
                            SpeakEnemyKOS(Actor);
                            return true;
                        }
                        else
                        {
                            return false;
                        }
                }
                else
                    return true;
            }
            else //if (Actor == "You" && ignoreYou == true)
            {
                return false;
            }
        }

        //private void RecentlyDetected_Refresh()
        //{
        //    //throw
        //    //System.Windows.Forms.ListBox.ObjectCollection objs = lbRecentlyDetected.Items;
        //    //foreach (object lio in objs)
        //    //{
        //    //    if (
        //    //}
        //}

        System.Speech.Synthesis.SpeechSynthesizer speaker = new System.Speech.Synthesis.SpeechSynthesizer();

        private void SpeakEnemyKOS(KOSPlayerInfo playerInfo)
        {
            if (cbSpeakEnemies.Checked)
            {
                if (playerInfo.SpeakText != null && playerInfo.SpeakText.Trim() !="")
                    speaker.SpeakAsync(playerInfo.SpeakText);
            }
        }
        private void SpeakEnemyKOS(string enemyKOSTarget)
        {
            if (cbSpeakEnemies.Checked)
            {
                if (kosList.ContainsKey(enemyKOSTarget))
                {
                    KOSPlayerInfo playerInfo = kosList[enemyKOSTarget];
                    speaker.SpeakAsync(playerInfo.SpeakText);
                }
            }
        }

        private void AppendDamageToTracker(ParsedLogEvent logDamageEvent)
        {
            //AppendTrackerMessage(logDamageEvent.TimeStamp, logDamageEvent.Actor + " damaged " + logDamageEvent.Target);
        }

        private void btnSpeakKOSItem_Click(object sender, EventArgs e)
        {
            KOSPlayerInfo playerInfo = (KOSPlayerInfo)clbKOSList.SelectedItem;
            if (playerInfo != null)
            {
                SpeakEnemyKOS(playerInfo);
            }
        }



        private void btnAddKOS_Click(object sender, EventArgs e)
        {
            if (tbKOSName.Text.Trim() == "")
            {
            }
            else if (tbKOSName.Text.IndexOfAny(new char[]{' ','-'})!=-1)
            {
                //dont add
                InfoLogIt("Player name cannot contains spaces or dashes.");
            }else
            {
                string enemy = ProperCasePlayerName(tbKOSName.Text.Trim());
                if (!kosList.ContainsKey(enemy))
                {
                    KOSPlayerInfo playerInfo = new KOSPlayerInfo(enemy, "Added on " + DateTime.Now.ToShortDateString(), "Activity by " + enemy + " detected.");
                    kosList.Add(enemy, playerInfo);
                    clbKOSList.Items.Add(playerInfo);
                    clbKOSList.SelectedItem = playerInfo;
                    SaveKOSListToFile();
                    clbKOSList.Refresh();
                }
            }
        }

        private void btnRemoveKOS_Click(object sender, EventArgs e)
        {
            KOSPlayerInfo info = (KOSPlayerInfo)clbKOSList.SelectedItem;
            kosList.Remove(info.PlayerName);
            clbKOSList.Items.Remove(info);
            pgKOSItemEditor.SelectedObject = null;
            clbKOSList.Refresh();
            pgKOSItemEditor.Refresh();
            SaveKOSListToFile();
        }

        private void clbKOSList_SelectedIndexChanged(object sender, EventArgs e)
        {
            KOSPlayerInfo pInfo = (KOSPlayerInfo)(clbKOSList.SelectedItem);
            if (pInfo != null)
            {
                tbKOSName.Text = pInfo.PlayerName;
                pgKOSItemEditor.SelectedObject = pInfo;
            }
        }

        private void pgKOSItemEditor_SelectedObjectsChanged(object sender, EventArgs e)
        {
            clbKOSList.Update();
        }

        //private void clbRecentlyDetected_SelectedIndexChanged(object sender, EventArgs e)
        //{
        //    if (clbRecentlyDetected.SelectedItem != null)
        //        tbRecentlyDetectedEntry.Text = clbRecentlyDetected.SelectedItem.ToString();
        //    else
        //        tbRecentlyDetectedEntry.Text = "";
        //}

        private void btnClearFocusTarget_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Right)
            {
                cmsThreatMeterTargets.Show(btnClearFocusTarget, new Point(0, 0));
            }
        }

        private void tsmiThreatMeterCopyPlayersToClipboard_Click(object sender, EventArgs e)
        {
            StringBuilder clipText = new StringBuilder("");

            foreach (string player in calc.htGroupThreatFromLog.Keys)
            {
                clipText.AppendLine(player);
            }
            Clipboard.SetText(clipText.ToString());
        }

        private void tsmiDamageMeter_CopyToClipboard_Click(object sender, EventArgs e)
        {
            StringBuilder clipText = new StringBuilder("");

            foreach (string player in calc.htTotalDamagesByPlayer.Keys)
            {
                clipText.AppendLine(player);
            }
            Clipboard.SetText(clipText.ToString());
        }

        private void tsmiCopyAllToClipboardplainText_Click(object sender, EventArgs e)
        {
            StringBuilder clipText = new StringBuilder("");

            foreach (string player in calc.htTotalHealsByPlayer.Keys)
            {
                clipText.AppendLine(player);
            }
            Clipboard.SetText(clipText.ToString());
        }

        private void tvLogFileThreat_Click(object sender, EventArgs e)
        {
            UpdateThreatTreeView();
        }

        private void tvTargets_Click(object sender, EventArgs e)
        {
            UpdateTargetsTreeView();
        }

        bool isHidden; 
        const string msg2 = "[<< KOS ]";
        const string msg = "[KOS >>]";
        private double CertainOutOfCombatTime_BetweenWrites = 10;

        private void btnSlider_Click(object sender, EventArgs e)
        {
            isHidden = !isHidden;
            if (isHidden)
            {
                scPVPTracker.Panel1Collapsed = true; ;
                //show new message and tooltip
                btnSlider.Text = msg;
            }
            else
            {
                scPVPTracker.Panel1Collapsed = false;
                btnSlider.Text = msg2;
            }
        }

        private void pbParseStatus_VisibleChanged(object sender, EventArgs e)
        {
            Debug.Assert(true);
        }


        //compare file date time stamp to see if its current
        // within some tolerance
        TimeSpan outOfCombatTime;
        const int durationOfActivity_seconds = 2;

        private DateTime lastDataRecieved = DateTime.MinValue;
        private bool SafeAutoDisableLogging
        {
            get { return tsmiAutoStopLogging.Checked; }
            set { tsmiAutoStopLogging.Checked = value; }
        }

        /*
        public bool CombatParsingActivity { 
            get {
                bool loggingActive = false;
                if (openFileDialog1.FileName == "")
                    return false;

                DateTime lastWriteTime = File.GetLastWriteTime(openFileDialog1.FileName);
                TimeSpan durationBetweenWrites =  DateTime.Now - lastWriteTime;
                if (durationBetweenWrites.TotalSeconds > CertainOutOfCombatTime_BetweenWrites)
                {
                    //are we sure of inactivity
                    //file stamp may not be accurate
                    TimeSpan durationLastRecieved = DateTime.Now - lastDataRecieved;
                    if (durationLastRecieved.TotalSeconds < durationOfActivity_seconds)
                        loggingActive = true;
                    else
                        loggingActive = false;
                }
                else
                    loggingActive = false;


                //draw more activity
                //if (cbLogging.Checked != loggingActive)
                //{
                //    cbLogging.Checked = loggingActive;
                //    cbLogging.Update();
                //}

                outOfCombatTime = durationBetweenWrites;
                return loggingActive;
            } 
        }*/

        private void pbThreat_Paint(object sender, PaintEventArgs e)
        {
            DrawThreat(true);
        }


        public object AddKnownPlayer(string playerName, bool updateListControl)
        {
            Player pNew = null;
            if (playerName != "")
            {
                char[] prohobitedChars = new char[]{'\'', '-', ' '};

                int ioAny = playerName.IndexOfAny(prohobitedChars, 0);
                if (ioAny == -1) //if free of those characters
                {
                    if (!playersList.ContainsKey(playerName))
                    {
                        pNew = new Player(playerName);
                        playersList.Add(playerName, pNew);
                    }
                    if (pNew != null && updateListControl)
                        PlayersListEditorControl.RefreshItems();
                }
            }
            return pNew; //PlayersListEditorControl.AddByItemByText(playerName, updateListControl);

        }

        //private void playersListEditorControl_SelectedIndexChanged(object sender, EventArgs e)
        //{
        //    System.Windows.Forms.ListView.SelectedListViewItemCollection objSelected = playersListEditorControl.SelectedItems;
        //    if (objSelected==null || objSelected.Count == 0)
        //    {
        //        pgEdi.SelectedObjects = null;
        //        return;
        //    }

        //    Player[] playersSelected = GetPlayersSelected(objSelected);
        //    pgSelectedPlayers.SelectedObjects = playersSelected;
        //}

        private Player[] GetPlayersSelected(System.Windows.Forms.ListView.SelectedListViewItemCollection objSelected)
        {
            if (objSelected == null)
                return null;

            List<Player> playersSelected = new List<Player>();

            foreach (ListViewItem item in objSelected)
            {
                if (item == null || item.Tag == null)
                {

                }else
                {
                    string key = item.Tag.ToString();
                    Player ptd = (Player)playersList[key];
                    playersSelected.Add(ptd);
                }
            }
            return (playersSelected.ToArray<Player>());
        }

        private void tsmiAutoClearThreat_Click(object sender, EventArgs e)
        {
            bool newState = !tsmiAutoStopLogging.Checked;
            tsmiAutoStopLogging.Checked = newState;
        }


        protected DateTime StartLoggingTime = DateTime.MinValue;

        public bool CurrentLogFileHasNotChanged {
            get {

                return File.GetLastAccessTime(CurrentLogFile) != StartLoggingTime;
            }
        }

        private void cbLogging_CheckedChanged(object sender, EventArgs e)
        {
            if (cbLogging.Checked)
                cbLogging.ForeColor = Color.Green;
            else
                cbLogging.ForeColor = Color.Gray;
        }

        private void pbThreat_MouseMove(object sender, MouseEventArgs e)
        {
            //const int durationOfTooltip = 7000; //ms
            EnemyPlayerPair underMouse = GetBarInfoFromClickedPoint(e.Location);
            if (underMouse == null)
                return;

            string enemyWhoHates = underMouse.Enemy;
            string player = underMouse.Player;

            if (player == null)
            {
            }
            else if (enemyWhoHates == null)
            {
            }
            else if (tcAllPages.SelectedTab == tpThreat)
            {
                if (calc.htGroupThreatFromLog != null && calc.htGroupThreatFromLog.ContainsKey(enemyWhoHates))
                {
                    Hashtable hateTable = (Hashtable)calc.htGroupThreatFromLog[enemyWhoHates];
                    long hateAmount = Convert.ToInt64(hateTable[player]);

                    long totalHate = 0;
                    long highestThreatByPlayer = 0;
                    string playerWithHighestThreat = null;
                    foreach (string playerKey in hateTable.Keys)
                    {
                        long amountThreat = Convert.ToInt64(hateTable[playerKey]);
                        if (amountThreat >= highestThreatByPlayer)
                        {
                            highestThreatByPlayer = amountThreat;
                            playerWithHighestThreat = playerKey;
                        }
                        totalHate += amountThreat;
                    }
                    StringBuilder tooltipText = new StringBuilder();
                    string verb = null;
                    if (player != "You")
                        verb = " has: ";
                    else if (player == "You")
                        verb = " have: ";

                    if (FocusTarget != null && FocusTarget != FocusAllString)
                        tooltipText.AppendLine("Target:\r\n" + enemyWhoHates + "\r\n");

                    tooltipText.AppendLine(player + verb + hateAmount + "/" + totalHate + " threat on target.");


                    if (hateAmount != highestThreatByPlayer)
                    {
                        string playersOwn = null;
                        float normalizedPercent = (int)(hateAmount / (float)highestThreatByPlayer * 10000f) / 100f;
                        if (playerWithHighestThreat == "You")
                            playersOwn = "Your";
                        else
                            playersOwn = playerWithHighestThreat + "'s";

                        tooltipText.Append(normalizedPercent + "% of " + playersOwn + " threat on this target.\r\n");
                    }
                    float percentOfTotal = (int)((float)hateAmount / totalHate * 10000f) / 100f;
                    tooltipText.Append(percentOfTotal + "% of the Total threat on this target.\r\n");

                    ttMainTooltip.SetToolTip(pbThreat, tooltipText.ToString());
                    //ttMainTooltip.Show(tooltipText.ToString(), this.toolStripMain);
                }
            }
        }

        private void tsmiAddCheckedToKnownPlayers_Click(object sender, EventArgs e)
        {
            List<TreeNode> toRemoveList = new List<TreeNode>();
            foreach(TreeNode threatNode in tvLogFileThreat.Nodes)
            {
                if (threatNode.Checked)
                {
                    AddKnownPlayer(threatNode.Text, true);
                    toRemoveList.Add(threatNode);
                }
            }

            foreach (TreeNode nodeToRemove in toRemoveList)
            {
                tvLogFileThreat.Nodes.Remove(nodeToRemove);
            }

            tvLogFileThreat.Update();
        }

        private void tcAllPages_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (tcAllPages.SelectedTab == tpDamageAndHealingTree)
            {
                UpdateDamageAndHealingTreeView();
            }
            else if (tcAllPages.SelectedTab == tpThreatTree)
            {
                UpdateThreatTreeView();
            }
        }

        private void btnRecentPlayers_Click(object sender, EventArgs e)
        {
            lbRecentlyDetected.Items.Clear();
            detectedActors.Clear();
        }

        private void btnPlayerFilter_Click(object sender, EventArgs e)
        {
            cmsPlayerFilters.Show(btnPlayerFilter, new Point(0, 0));
        }

        private void cbSimulationEnabled_CheckedChanged(object sender, EventArgs e)
        {            
            this.mParser.DelayPerLine_ForSimulation_InMilliSeconds = (int)nudParsePeriod.Value;
            this.mParser.RealTimeSimulation = cbSimulationEnabled.Checked;
        }

        private void nudSimpleSimulation_Period_ValueChanged(object sender, EventArgs e)
        {
            this.mParser.DelayPerLine_ForSimulation_InMilliSeconds = (int)nudParsePeriod.Value;
        }

        private void tmrGraphUpdater_Tick(object sender, EventArgs e)
        {
                UpdateNecessaryPages(false);
        }

        private void frmMeter_KeyDown(object sender, KeyEventArgs e)
        {
            if (cbSimulationEnabled.Checked)
            {
                if (e.KeyCode ==  Keys.N)
                {
                    mParser.GotoNextEvent();
                }
                if (e.KeyCode == Keys.H)
                {
                    mParser.SkipAheadHour();
                }
                if (e.KeyCode == Keys.M)
                {
                    mParser.SkipAheadMinute();
                }
                if (e.KeyCode == Keys.P)
                {
                    mParser.RealTimeSimulationPaused = !mParser.RealTimeSimulationPaused;
                }
            }
        }

        private void SkipAheadHour()
        {
            mParser.SkipAheadHour();//forward to mParser class
        }

        private void GotoNextEvent()
        {
            this.mParser.GotoNextEvent();//forward message access to member data does not belong here
        }

        private void nudParsePeriod_ValueChanged(object sender, EventArgs e)
        {
            tmrLogUpdater.Interval = (int)nudParsePeriod.Value;
        }

        private void nudGraphUpdaterRefreshInterval_ValueChanged(object sender, EventArgs e)
        {
            tmrGraphUpdater.Interval = (int)nudGraphUpdaterRefreshInterval.Value;    
        }

        private void nudTreeViewRefreshInterval_ValueChanged(object sender, EventArgs e)
        {
            tmrUpdateTreeViews.Interval = (int)nudTreeViewRefreshInterval.Value;
        }

        private void nudFrequencyOfSimulation_ValueChanged(object sender, EventArgs e)
        {
            mParser.SimulationFrequency = (float)nudFrequencyOfSimulation.Value;
        }

        private void tsmiParseSimulation_Click(object sender, EventArgs e)
        {
            cbSimulationEnabled.Checked = true;
            cbSimulationEnabled.Enabled = true;
            OpenUserSpecifiedFile();

        }


        private bool exiting = false;
        public bool Exiting
        {
            get { return exiting; }
            set { exiting = value; }
        }

        private void tsmiClearThreatTable_Click(object sender, EventArgs e)
        {
            ClearThreatTableOfFocus();
        }

        private void ClearThreatTableOfFocus()
        {
            if (calc.htGroupThreatFromLog.ContainsKey(FocusTarget))
                calc.htGroupThreatFromLog.Remove(FocusTarget);
        }

        private void toolStripMenuItem1_Click(object sender, EventArgs e)
        {
            HelpBrowser hbrowser = new HelpBrowser();
            hbrowser.Show();
        }

        private void tsddbOptions_Click(object sender, EventArgs e)
        {

        }


    } //end of   class
} //end of namespace
